異体字セレクタってなあに


ある日隣の席でテストをしてくれていたQA要員がこんな挙動を見つけました。

QA「あれ……文字が増えるっ!?」

コレの続き。
https://qiita.com/wingsys/items/81d46451d8b93ab065de

増える!?

確認してみると、問題の部分はこれまでにも何度か妙な挙動をしていた入力部でした。
該当部分は「その文字がEUC-JPで取り扱えるなら許容、そうでないならエラーを返しつつ、取り扱えない文字をゲタ (〓) に差し替えて返す」という地味に面倒な仕様。まだまだバグが潜んでいても驚くことではありませんでした。

文字が増えるというのは奇妙な話です。
話を聞いて、とりあえず私が疑ったのは以下の2つ。

  1. サロゲートペアで表現される文字列が投入された。
    その時のバリデータ兼フォーマッタ (不健全極まりない!) にはバグがあり、サロゲートペアを正常に取り扱えないので、たしかに文字数は増える。
  2. 何らかの原因でゼロ幅スペースが紛れ込んだ。
    EUC-JPで取り扱えたかどうかは自信がなかったものの、単に文字数が増えたという意味ではありうる話。

しかし、これでは説明できませんでした。
入力された文字は「渡邉󠄂」。これが「渡邉〓〓」となっていたのです。

とりあえずUnicode

しかし、我々には強い味方がいます。そう、Unicode表記への変換です。
詳しく聞いてみると、彼は「渡邉っていろんな字があるけど、耐えられるのだろうか」と思い立ち、様々なバージョンの文字表記をテキストで公開しているWebページからこれをコピペして打ち込んでいたのでした。

果たして、結果は \u6e21\u9089\udb40\udd02 。4文字です。
はて、 \udb40 に \udd02 ? ずいぶんと外れの方にあるものが出てきたな。一体これは?
白状しますが、人生初の異体字セレクタとの遭遇でした。

前回までのUnicodeは

そもそもの話、もともとのUnicodeは65535文字を表現可能でした。16ビットですね。
彼らは「これだけあれば世界中の文字を収録できる!」と思ったのでしょう。事実、どう考えても実用文字かといわれると「?」な錬金術文字やらルーン文字やらまでもが収録されています。

しかし、現実は厳しいものだったのは言うまでもありません。
たかだか16ビットぽっちではとてもとても収録しきれないことがわかってきます。そりゃあまあ、特にアジア圏の文字ってやたら頭数多いですしね。
Unicode側も「同じ形の文字複数も登録できっかよぉ!?」なんて全力で抵抗しますがやっぱり足らず、サロゲートペアという形で容量を追加。
世の中の文字列処理に悲鳴をあげさせつつもなんとかして、やれやれ……というのが、前回までのお話。

多すぎる漢字のバリエーション

世の中、少なくとも日本には、異体字と呼ばれるものがなんかもう凄まじく大量にあります。
そのもっともわかり易い例が先程の「渡邉」に始まり、「高橋」「斎藤」「吉田」あたりの微妙に違う人名たちです。
サロゲートペアで容量が増えたといえど、これらの字を片っ端からバカ正直に収録していてはまたぞろ不足しかねません。

そこで登場したのが「異体字セレクタ (Variation Sequence) 」です。
(実は2種類あるのですが、日本では漢字専用版であるIVSがそれとして広く浸透してしまった結果、異体字セレクタ=IVSという誤解も見受けられます)

異体字セレクタは、文字の後ろにくっつけて使います。
これにより「この文字は実はこういうタイプなんだよ!」「な、なんだってー!?」とソフトウェア側が知る機会を提供します。
感覚的にはサロゲートペアと似ていますが、くっつける対象の「前の文字」が単独で機能すること、なにより指定された異体字がフォントやソフトウェアによってサポートされていない率が高いことから、それなりに面倒な代物です。

先の例では、渡邉の「邉」に異体字セレクタがくっつき、そのセレクタ自身がサロゲートペアによって表現されていたために2文字が4文字に化けていたのです。

私「まあ、発生率も低いだろうし大丈夫じゃないの? こないだのチケットにコメント付けておけばいいよ」
QA「了解っす」

オチ

QA「大変です! 変換1発で出てくる文字列に異体字セレクタが!」
私「何だって……それは一体何さ!?」
QA「麴町の麴……」
私「……アレ、旧字の方だったのか……」

どっとはらい。