环境
操作系统Win10 1909,PowerShell版本($PSVersionTable.PSVersion.ToString()
)6.2.3。$PSCulture
和$PSUICulture
都是ja-JP
,用自带终端(这倒不重要。为了避免问题,用例都是在操作文件)。
缘由
可能因为大字报的风格影响到了弹幕(所谓Big Shita Red,底端大红字),“打 字 带 空 格”这样的玩法(起码以前)非常流行。我寻思拿PowerShell整一个玩。
TL;NR
下面的实现其实只是基于code point(码位)而言的,实际上我们更希望基于grapheme(日语那边叫書記素)操作——毕竟我们认为的“一个字”,其实表现出来就是“一个grapheme”而已。
顺便,找到一些好文章:
总而言之,现在看来最方便的办法其实是这样的:
# .\sample.txt
# 🎢柑橘峨嵋酒🎢
#
@([System.Globalization.StringInfo]::GetTextElementEnumerator((Get-Content .\sample.txt))) -join ' ' > .\b.txt
# .\b.txt
# 🎢 柑 橘 峨 嵋 酒 🎢
#
其实用System.Globalization.StringInfo
就足够了。文档参见这里。
另外,最彻底的办法应该是ICU(International Components for Unicode,不是那个996哦),这边有用例。
啊,我之前大概是做了一些无用功,不过代码倒是值得一看。
代码
D:\work\util\U8ChrList.ps1
<#
U8ChrList Class
#>
class U8ChrList : System.Collections.Generic.List[string] {
# separator
static [string]$Separator = ' ';
# constructor
U8ChrList([string]$s) {
[string]$tmp = '';
$s.ToCharArray() |
ForEach-Object {
if ([char]::IsHighSurrogate($_)) {
$tmp = [string]$_;
} elseif ([char]::IsLowSurrogate($_)) {
$this.Add($tmp + [string]$_);
} else {
$this.Add([string]$_);
}
};
}
# implement w/ U8ChrList object
[string] InsertSeparator() {
return $this.ToArray() -join [U8ChrList]::Separator;
}
# static implement w/o U8ChrList object
static [string] InsertSeparator([string]$s) {
[int]$Script:charCounter = 0;
$matchEvl = {
[string]$t = $args[0].Groups[0].Value;
$Script:charCounter += 1;
if ($Script:charCounter -eq $s.Length `
-or [char]::IsHighSurrogate([char]$t)) {
return $t;
}
return $t + [U8ChrList]::Separator;
};
[string]$tmp = ([regex]'.').Replace($s, $matchEvl);
Remove-Variable 'charCounter' -Scope 'Script';
return $tmp;
}
}
用例
打开pwsh终端,临时加载驱动器:
New-PSDrive -Name 'W' -PSProvider FileSystem -Root 'D:\work';
顺便cd过去:
Set-Location W:
导入模组:
Import-Module .\util\U8ChrList.ps1
顺便查看一下:
Get-Module -Name U8ChrList
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0 U8ChrList
在D:\work\
下,建立sample.txt
,内容如下:
🎢柑橘峨嵋酒🎢
正戏(D:\work\a.txt
还不存在):
$o = New-Object U8ChrList($s)
$o -join ' ' >> .\a.txt
$o.InsertSeparator() >> .\a.txt
[U8ChrList]::InsertSeparator($s) >> .\a.txt
查看一下D:\work\a.txt
:
🎢 柑 橘 峨 嵋 酒 🎢
🎢 柑 橘 峨 嵋 酒 🎢
🎢 柑 橘 峨 嵋 酒 🎢
👌です!
后记
至于说为什么写得这么复杂,原因在于.NET自己没提供Utf8String之类的方法,导致遇到超过BMP范围的文字就一定要注意代理对了。
其实吧,要是用(现代的)JavaScript:
console.log([...'🎢柑橘峨嵋酒🎢'].join(' '));
输出
🎢 柑 橘 峨 嵋 酒 🎢
是不是爽多了。
以后再试着分析一下子。