窓辺の小石 第212回 分解された文字
2025年4月18日(金)13時2分 マイナビニュース
ユニコード文字では、複数の文字(コードポイント)を組み合わせて1つの文字とすることができる。たとえば、人物の絵文字では、スキントーン指定文字(EMOJI MODIFIER FITZPATRICK TYPE-x)を、使って肌の色を指定できる。さらに、ZERO WIDTH JOINERを使って複数の人物を組み合わせることができる。こうして作られた「文字」は、1つの絵文字になるが、複数のコードポイントから構成される。こうしたまとまりを「書記素」(Graphem。グラフィーム)という。
かつては文字は、バイトやワードで表現されていたが、今や文字列処理は、そういう簡単なものではなくなってきた。
ここでは、ターミナル内でPowerShell(Ver.7.5)を使い、文字列を書記素単位で扱ってみる。まずは、対象となる文字列は、$xに入っているとする(写真01)。ここで入れた絵文字は、3人の人物(ADALT、BOY、GIRL)のそれぞれにスキントーンの指定があり、3人をZERO WIDTH JOINERで結合させている。1つの絵文字だが、8つのユニコードコードポイントから構成されている。
$xに対してParseCombiningCharactersメソッドを使い書記素の開始位置を$pccに記憶させておく。
$pcc=([System.Globalization.StringInfo]::ParseCombiningCharacters($x))
文字列中の書記素の開始位置が$pccに配列として記録され、書記素の数は、「$pcc.count」で求めることができる。
以後の扱いを考えると、書記素ごとにオブジェクトを作ると、以後の処理が簡単になる。まずは、Unicode(Windows標準のUTF-16LEエンコーディング。以後WindowsのUTF-16LE文字をUnicode文字と表記する)でオブジェクトを作り、結果を$ucに格納しておく。
$uc=$pcc | %{$temp=[System.Globalization.StringInfo]::GetNextTextElement( $x,$_);[pscustomobject]@{code=[int[]][char[]]$temp ;letter=$temp} }
以後は、$uc.codeでUnicode文字コード、$uc.letterで書記素文字にアクセスできる。16進数で文字コードをダンプしたければ、
$uc | %{ $_.code | %{$_.ToString("X4")};" "}
とすればよい。
書記素の各Unicode文字をコードポイントで表示したい場合、StringクラスのEnumerateRunesメソッドを使うと簡単だ。前記と同じく、書記素ごとに文字をコードポイントに変換するには、
$cp=$pcc | %{$temp=[System.Globalization.StringInfo]::GetNextTextElement( $x,$_);[pscustomobject]@{code=$temp.EnumerateRunes().value ;letter=$temp} }
とする。こちらも前記と同じ方法で16進数ダンプが行える。
また、Unicode文字に対して、CharUnicodeInfoクラスのGetUnicodeCategoryメソッドを使うと、文字のカテゴリを表示できる。
$ucl=$uc[1].letter; 0..(([char[]]$ucl).count-1) | % { "$(([int][char]$ucl[$_]).tostring("X4")):$([System.Globalization.CharUnicodeInfo]::GetUnicodeCategory($ucl,$_))" }
Charクラスにも同名のメソッドがあるが、準拠しているユニコードのバージョンが異なる。CharUnicodeInfoクラスは、ユニコードのバージョンアップに追従するが、Charクラスは、古い仕様のユニコードのままになっている。
今回のタイトルネタは、アルフレッド・ベスター(Alfred Bester)の「分解された男」(創元推理文庫。原題The Demolished Man、1953年)である。人の心を読むことができるESPERの警察官と通常人の犯罪者(主人公)の対決を描く。普通なら通常人の方が分が悪いはずだが、主人公はさまざまな手を使い心を読ませない。エスパーもののスタイルを作り、のちの作品に大きな影響をあたえた。