🦔
【PowerShell】濁音・半濁音と Unicode 正規化
macOS のファイル名のなかには NFC・NFD 形式の濁音、半濁音の文字が混在していることがある。NFC では1つのコードポイント、NFD では基底文字と結合文字の2つのコードポイントで構成される。Unicode 正規化には Normalize
を使う。
"が".Normalize([Text.NormalizationForm]::FormD).Length
2
> "か`u{3099}".Normalize([Text.NormalizationForm]::FormC).Length
1
かなや濁点・半濁点は基本多言語面の範囲(U+000..U+FFFF)にあるので、文字数を Length でカウントしたが、一般的な文字数を数えるには EnumerateRunes
か 後述するGetTextElementEnumerator
を使う
"🐙と🦑".Length
5
"🐙と🦑".EnumerateRunes().Value.Length
3
Unicode 正規化で文字がどのように置き換わったのか確認してみる
echo が |
>> %{ $_.Normalize([Text.NormalizationForm]::FormD) } |
>> %{ $_.EnumerateRunes().Value } |
>> %{ "U+{0:X}" -f $_ }
U+304B
U+3099
NFD 形式の濁音・半濁音が混在していることを想定して文字列を処理するのであれば、正規表現の文字クラスを使って適用範囲を制限する。ひらがなもしくはカタカナと濁点もしくは半濁点で構成される書記素クラスターの正規表現は [\p{IsHiragana}\p{IsKatakana}]\p{Mn}
であらわすことができる。
-replace
演算子を使って置き換える例を示す
> $str = "は`u{3099}ハ`u{3099}と神"
> $str.length
6
> $ret = $str -replace '[\p{IsHiragana}\p{IsKatakana}]\p{Mn}', `
{ $_.Value.Normalize([Text.NormalizationForm]::FormC) }
> $ret.length
4
書記素クラスターの数を数えるには LengthInTextElements
を使う
echo がぎぐ |
>> %{ $_.Normalize([Text.NormalizationForm]::FormD) } |
>> %{ ([Globalization.StringInfo] $_).LengthInTextElements }
3
書記素クラスター単位で何らかの処理を行うには GetTextElementEnumerator
を使う
echo がぎぐ |
>> %{ $_.Normalize([Text.NormalizationForm]::FormD) } |
>> %{ [Globalization.StringInfo]::GetTextElementEnumerator($_) }
が
ぎ
ぐ
書記素クラスターが2つのコードポイントで構成されており、2つめのコードポイントの値が 0x3099
もしくは 0x309A
である場合に Unicode 正規化を適用すればよい
Discussion