「1文字」の数え方は一つではない
画面で一つに見える絵文字が、内部では複数の符号からできていることがあります。例えば「👩🏽❤️💋👨🏻」は、Unicodeのコードポイントで数えると10個です。一方、通常の書記素クラスタの区切りでは一まとまりとして扱われます。「本当は何文字か」という問いには、まず何を一単位として数えるかを決める必要があります。
これは絵文字だけの特殊事情ではありません。「が」も、一つの符号で表す方法と、「か」に結合用の濁点を続ける方法があります。見た目に近い数え方と、保存や通信に必要な数え方が一致しないのです。
四つの物差しで同じ文字列を数える
| 文字列 | コードポイント数 | UTF-16符号単位数 | UTF-8バイト数 | 書記素クラスタ数 |
|---|---|---|---|---|
| A | 1 | 1 | 1 | 1 |
| 😀 | 1 | 2 | 4 | 1 |
| 👍🏽 | 2 | 4 | 8 | 1 |
| 🇯🇵 | 2 | 4 | 8 | 1 |
| 👨👩👧👦 | 7 | 11 | 25 | 1 |
| 👩🏽❤️💋👨🏻 | 10 | 15 | 35 | 1 |
コードポイントは、Unicodeの符号位置です。U+1F600のような番号で識別します。UTF-16の符号単位は16ビットの単位で、😀のような文字は二つの符号単位を使います。UTF-8のバイト数は保存形式に依存するデータ量です。「1文字=1バイト」や「絵文字=必ず2文字」という決め方では、表の全部を説明できません。
書記素クラスタは、利用者が一文字と感じるまとまりに近づけるための区切りです。表示される絵の個数そのものを数えているわけではなく、Unicodeの規則に従って境界を決めます。未対応の端末で絵文字がばらばらに描画されても、同じ規則で区切れば一クラスタになる場合があります。
10個の符号が、一つの絵になる例
「👩🏽❤️💋👨🏻」を順に分解すると、女性、肌の色、ZWJ、ハート、絵文字表示用の異体字セレクター、ZWJ、キスマーク、ZWJ、男性、肌の色となります。ZWJはU+200Dのゼロ幅接合子で、対応する組合せを一つの絵として表示する手掛かりです。見えない符号も数に含まれるため、単に画面の人物や記号を数えても10にはなりません。
国旗「🇯🇵」は別の仕組みです。地域指示記号のJとPに相当する二つのコードポイントを組み合わせています。親指と肌の色の組合せも、ZWJを挟む家族の組合せとは構造が異なります。絵文字の部品を好きな順番で連結すれば何でも新しい絵になるわけではありません。
文字数制限がサービスごとに違う理由
投稿欄が数えているのは、書記素クラスタかもしれませんし、コードポイントや独自の重みかもしれません。データベース側には別にバイト数制限があることもあります。「この絵文字を一つ入れたら残りが二つ減った」という現象だけで、文字が壊れているとは判断できません。文字数制限の仕様を確認するのが確実です。
プログラムでも、単純な長さ取得が見た目の文字数になるとは限りません。例えばJavaScriptの文字列のlengthはUTF-16の符号単位数を返します。コードポイント単位の反復で数え直しても、複合絵文字を一つと数えるには不十分です。書記素単位の処理には、Intl.Segmenterなど区切りのための機能を使います。
途中で切らないことが大切
文字列を先頭から何バイト、あるいは何符号単位まで、と機械的に切ると、一文字の符号化や絵文字の組合せの途中で切れる場合があります。省略表示やカーソル移動では書記素境界を考慮し、保存容量ではバイト数も確認する、という使い分けが必要です。
なお、書記素の区切りはUnicodeの版や実装によって対応範囲が変わることがあります。表の例は確認時点の環境で、コードポイント数・UTF-16長・UTF-8長を実測し、書記素分割も照合しています。「絵文字はすべて10文字」ではなく、一つの見た目を、複数の符号で表すことがあると理解すると整理しやすくなります。
関連する雑記
参考資料
資料確認日:2026年10月2日
