Windowsで文字列を扱う際UTF-16を使っているが、ユーザ入力データを扱う場合まともには扱えないということがわかった。
サロゲートペア
UTF-16でも1文字は2バイトで表現しきれないので、サロゲートペアという苦肉の策が取られた。
これは今までのShiftJISなんかと同じで、最初のコードがこの範囲ならその後も含めて1文字と表すような感じで、ありがたみがない。
ただShiftJISと違って普段使うほとんどの文字は2バイトで表せるため、仮にサロゲートペアに対応してなくても不具合が顕在化しない場合もある。
内部処理的には問題はほとんど起きないが、文字数を制限するような場合に問題になる。
サロゲートペアが文字列中に含まれていると、含まれている文字数分文字数が増えてしまう。
10文字制限の場合、サロゲートペアが1文字含まれていると9文字入力した時点で制限にかかってしまい、ユーザの見た目とギャップが生まれる。
また、EditBoxなどに任せずに自前で1文字ずつ追加、削除をする場合、サロゲートペアを考慮してない場合は上位のみ削除して下位が残ったままだと表示に影響が出る。
サロゲートペアだけであれば、適切に範囲チェックを行い、文字数のカウントもサロゲートペアを考慮してやれば比較的簡単に対応できる。
ただ問題なのが結合文字や絵文字だ。
結合文字 合字 絵文字
例えばMS-IMEで、「あ」を入力して変換確定後、続けて「3099」と入力してF5を押下すると、「あ」とこのコードが合体して「あ゙」となる。
合体させるのはいくらでもよくて、ここに書かれているようなコードを何回も同じように付け足すと、どんどん付け足されて1文字扱いとなる。
この仕様はやばい。
どうやら、単純なルールは存在せず、ここに書かれていることをすべて理解して実装すれば文字の区切り判断はできるらしい・・・が、そんなことやってたら時間が足りないし、今後も仕様が追加されていくので実装しきれない。
ICU
というわけで、ICUというライブラリの出番。
この中のICU4CというのがC/C++用のライブラリなのでGitHubからVisualStudioで開いてみた。
allinoneというソリューションを開いてビルドすると何やらいろいろビルドされた。
やりたいことは「書記素単位で文字を取得する」ということになるので、インクルードファイルは「ubrk.h」、ライブラリは「icuuc.lib」でコンパイルは通った。
早速動かしてみると、「icuucNN.dll」がないと怒られる。
(NNはバージョン。試したときは74)
binから持ってきて再実行すると、「icudtNN.dll」がないと怒られる。
binから持ってきて再実行すると、U_MISSING_RESOURCE_ERRORというエラーになる。
icudt.dllはunicodeのデータが入っていて、本来は20MBくらいあるみたい。
自分の環境で出来上がったファイルは3KBしかなく、中身がなさそう。
どうやったら中身がある状態でビルドできるのかわからなかった。
ファイル指定もできるらしく、chromiumとかに含まれているicudt.datとかがおそらくこれのことっぽい。icudt.datを配置してu_setDataDirectoryで指定してみたけどうまく動かなかった。
次に試したのがnugetにあるicu4cのライブラリ。
古いものしかなくて一番新しそうな58というのをインストールしてみた。
こっちは問題なく動いた。icudt58.dllのサイズは19MBぐらい。
さらに調べてみると、Windows10以降にはマイクロソフト版ICUが含まれているらしい。
何も入れる必要ないみたいだ。
早速、インクルードファイル「icu.h」、ライブラリファイル「icu.lib」で試したら問題なく動いた。
「あ゙」はもちろん、「👨👩👧👦」も1文字として判定できるようになった。
対処してないと、「あ゙」は2文字、サロゲートペアを考慮しても2文字、「👨👩👧👦」は11文字、サロゲートペアを考慮しても7文字として判定されてしまう。
0 件のコメント:
コメントを投稿