まずは以下をご覧ください。
私がこんな感じで、入力数の試験をしていたときのことです。
この絵文字👨👩👧👦が、11 文字の判定をされてしまうことに気が付きました。
え(笑)、なんか面白いなーと思いつつ、すこし困ったなーとも感じます。
試しに、文字数上限までコピペしてみましょう。
4 文字しか入れていないのに、もうすぐ上限になってしまいます。
「あと 6 文字分しか入力できないから、貼りつけできないだろうな」
─── と思って、貼りつけてみると……
???
!!!??
家族が減りました(笑)
今日は、私がしばらく笑いころげていた、この現象についてお話ししたいと思います。
結合絵文字について
複数の絵文字が結合されて、ひとつにみえる絵文字が存在するようです。
例えば;
👨👩👧👦(家族)
👩💻(女性エンジニア)
🧑🚀(宇宙飛行士)
これらは「複数の絵文字 + ZWJ(ゼロ幅結合子)」の結合で生成されています。UI 上の見た目と、内部の情報量が違うわけですから、ちぐはぐになる現象をひきおこします。
「結合絵文字」というのは私の造語です。技術的には、「ZWJ で結合された絵文字シーケンス」と呼ぶのが間違いのない言いかたになるようです。(難しいね)
ZWJ(ゼロ幅結合子)とは何か?
ZWJ は Zero Width Joiner(U+200D)のことで、こんな性質をもちます。
・幅を持たない(表示されない)
・前後の文字を結合して、1 つの絵文字として描画する
つまり、「👨 + ZWJ + 👩」で、「👨👩」として描画します。
私が「家族が減ったんだけど」と笑いころげていた👨👩👧👦も、実体(コードポイント列)は以下のようになり、合計 7 コードポイントとなります。
U+1F468(👨)
U+200D(ZWJ)
U+1F469(👩)
U+200D(ZWJ)
U+1F467(👧)
U+200D(ZWJ)
U+1F466(👦)
エンコーディングによって情報量は違う
先ほど、「合計 7 コードポイントとな」ると書きましたが、それでは私が体験した「11 文字の判定をされてしまう」ことの説明にはなりません。
実は、エンコーディングごとに "長さ" の意味が異なります。
内部(コードポイント)は;
👨 + ZWJ + 👩 + ZWJ + 👧 + ZWJ + 👦
= 合計 7コードポイント
─── これは、Unicode で換算した場合です。
●UTF-16 だと
絵文字 = 2 ユニット、ZWJ = 1 ユニット
→合計:11 ユニット(22 bytes)
●UTF-8 だと
絵文字 = 4 bytes、ZWJ = 3 bytes
→合計:25 bytes
私が作成していた Web アプリは、UTF-16 単位のため、👨👩👧👦は 22 bytes となり 11 文字の判定をされていたということです。
--
ということで、そういう絵文字がある、というお話でした。内部の仕組みを知ると、このケースもなぜそうなったのかが分かりますね。
6 文字しか残数がなかったから;
👨 + ZWJ + 👩 + ZWJ
までしか貼りつけできず、「👨👩」としてレンダリングされた、というワケです。
以上です。
Appendix
ほかにも、絵文字の長さが増えるパターンがあるのですが、私が驚いた「スキントーン」について紹介させてください。文字通り、「肌の色合い」です。
以下のご覧ください。
👍という絵文字は、2 ユニットですので、2 文字として判定されそうなものです。しかし、そうなっていません。なぜか 4 文字の判定をされてしまっています。
これがスキントーンと呼ばれるもので;
U+1F44D(👍)
U+1F3FD(茶色)
👍 + [茶色] = 👍🏽 で4ユニットとなるのです。