はじめに
二人を最初から一つにしておけば、何も問題など起こらなかったのだ。
一つのものを二つに分けたその時から、長い物語が始まる。
おや?また会いましたね。どうも、文字コードおじさんです。
もう半年近くも前になりますが、前回の投稿はだいぶ反響があったようで驚いております。ありがとうございます。
今回も文字コードネタですが、Unicodeにおける結合文字列を取り上げてみようと思います。
高度に発達した文字コードは地獄と見分けがつかない
次の画像はTwitterの投稿フォームですが、おかしな点があります。わかりますか?
そうです。4文字しか入力していないはずなのに 5文字分とカウント されていますね。おかしいと思いませんか?あなた?
改行とかスペース入れてるんじゃねーだろうな?とか言わないで下さいね?
さらに次の画像を見てみてください。
こちらは4文字とカウントされていますね。先のものとは一体何が違うのでしょうか?
とりあえず文字列を解析してみます。まずは4文字とカウントされている方から見てみましょう。
何の変哲もない解析結果なのです。
次に5文字とカウントされているものを見てみましょう。
3文字目が 「で」 ではなく 「て」 になっていますね。さらにその直後に 「COMBINING KATAKANA-HIRAGANA VOICE SOUND MARK」 なる怪しげな文字=濁点がいますね・・・・。
奴の正体は結合文字列
結合文字列とは?
さて、5文字とカウントされた 「なのです」 ですが、これの正体は 「結合文字列(Combining Characters / Combining Character Sequence)」 といいます。いやまぁ、タイトルで出とるやんけという話ですが、結合文字列とは平たく言ってしまうと、複数の文字を使って見かけ上の1文字を表現する仕組みと考えてもらって構いません。
この例だと 「て(U+3066)」 の後に、 「濁点(U+3099)」 を配置することによって 「で」 を表現していますね。 表示上は1文字のバージョンである 「で(U+3067)」 と全く区別がつかないですね。
先行する「て(U+3066)」を 「基底文字(Base Character)」 といいます。また、後続する「濁点(U+3099)」を 「結合文字 (combining character)」 といいます。ちなみに今回の例では結合文字が1つでしたが、 複数個続くケースもあります。
他方、「で(U+3067)」を 「合成済み文字(precomposed character)」 といいます。こちらの方が我々にはなじみ深いものだと思います。なお、Unicodeにおいては結合文字列を使うか、合成済み文字を使うかはデータ作成者の自由に任せられています。
結合文字列がもたらす地獄
見た目の区別がつかない
おなじ入力フォーム内に並べてみましたが、まぁ見た目じゃ区別つかないですよね。目検なんて非生産的なことをやっているとあっという間に地獄行きになる事案です。
検索でマッチしない
結合文字列である「て(U+3066)」と「濁点(U+3099)」に対して、合成済み文字である「で(U+3067)」で検索をしても一切マッチしません。まぁ、当然ですよね。同じに見えるのは見た目だけであって、中身であるコードポイントはまるで違うわけですから。
この問題に対処するには、「正規化」という手段を用います。今回は詳しく触れませんが、ものすごくざっくり言ってしまうと、「結合文字列に変換」とか「合成済み文字に変換」してから検索を行います。
何文字とカウントする?
フォームなどで入力文字数を制限する時に、結合文字列をどう扱うかが問題となります。
見かけ上は確かに1文字ですが、それを正確にカウントしようと思うとコードポイントを先頭からいちいち見て行って、結合文字はカウントしないといった処理が必要になるでしょうね。
ところで、Twitterでは結合文字が1文字とカウントされていましたね。個人的にはそれでいいと思います。
なんでこんなものを作った!言え!!
これは複数の音符を結合して見かけ上の1文字を作ってしまったという超おぞましい事案です(挙句こいつら全部4バイト文字/サロゲートペアなんだぜ・・・)
幸いなことに、滅多に出くわすことは無いと思いますが、どうしてこうなった!!
まとめ
さて、今回もおぞましいものを見てきたわけですが、まとめると
- 結合文字列は「基底文字」の後ろに「結合文字」を1個以上配置し、見かけ上の1文字を作り出す仕組み
- 結合文字列と合成済み文字のどちらを使うかは自由
- 結合文字列と合成済み文字は見かけ上は区別がつかない
- 結合文字列と合成済み文字が混在していると検索時にマッチしないなどの問題が発生する。問題を回避するには正規化が必要
- 結合文字列を見かけ上の文字数通りにカウントしようとすると面倒な処理をしなければならない
こんなところでしょうか。いやぁおそろしいですね。
二人を最初から一つにしておけば、何も問題など起こらなかったのだ。
一つのものを二つに分けたその時から、長い物語が始まる。
まさにこんな気持ちです。
ではまた次回。