絵文字の文字数カウント|1 文字に見えて複数文字?仕組みを解説
SNS やメッセージアプリで絵文字を使ったとき、文字数が予想以上に増えた経験はありませんか。全角と半角の文字数の違いと同様に、絵文字の文字数カウントにも独特の仕組みがあります。Unicode や文字コードの入門書と合わせて読むと、理解がより深まります。
絵文字の歴史と Unicode バージョン別の推移
世界初の絵文字は 1999 年、NTT ドコモの栗田穣崇氏が i-mode 向けに作成した 176 種類のセットです。12×12 ピクセルのドット絵として誕生した絵文字は、当初は日本の携帯キャリア各社 (ドコモ・au・ソフトバンク) が独自に実装しており、キャリア間で絵文字が文字化けする問題が日常的に発生していました。この互換性問題を解決するため、Google と Apple が Unicode Consortium に絵文字の標準化を提案し、2010 年の Unicode 6.0 で 722 種類が正式採用されました。
以降、Unicode のメジャーバージョンごとに絵文字は増加を続けています。
| Unicode バージョン | リリース年 | 追加された絵文字数 | 累計絵文字数 (推定) |
|---|---|---|---|
| 6.0 | 2010 | 722 | 722 |
| 7.0 | 2014 | 250 | 約 1,000 |
| 8.0 | 2015 | 41 | 約 1,050 |
| 9.0 | 2016 | 72 | 約 1,100 |
| 11.0 | 2018 | 157 | 約 1,600 |
| 13.0 | 2020 | 117 | 約 3,300 |
| 15.0 | 2022 | 31 | 約 3,600 |
| 16.0 | 2024 | 8 | 約 3,790 |
近年は新規追加数が減少傾向にあります。Unicode Consortium は絵文字の提案に対して「既存の絵文字の組み合わせで表現できないか」を厳しく審査しており、ZWJ シーケンスによる合成で対応可能なケースは新規コードポイントの割り当てを見送る方針を取っています。提案から正式採用までには約 2 年のプロセスが必要で、使用頻度の予測データや他の絵文字との差別化の根拠を提出しなければなりません。
エンコーディング別のバイトサイズ実測データ
絵文字は Unicode の基本と文字コードに基づいてエンコードされますが、エンコーディング方式によってバイトサイズが大きく異なります。以下は代表的な絵文字の実測データです。
| 絵文字 | 見た目 | コードポイント数 | UTF-8 バイト数 | UTF-16 バイト数 | UTF-32 バイト数 |
|---|---|---|---|---|---|
| 😀 (U+1F600) | 1 文字 | 1 | 4 | 4 | 4 |
| 👍🏻 (U+1F44D U+1F3FB) | 1 文字 | 2 | 8 | 8 | 8 |
| 👨👩👧👦 | 1 文字 | 7 | 25 | 22 | 28 |
| 🇯🇵 (U+1F1EF U+1F1F5) | 1 文字 | 2 | 8 | 8 | 8 |
| 1️⃣ (U+0031 U+FE0F U+20E3) | 1 文字 | 3 | 7 | 6 | 12 |
| 🏳️🌈 | 1 文字 | 4 | 14 | 12 | 16 |
UTF-8 では基本多言語面 (BMP) 外のコードポイントは 1 つあたり 4 バイトを消費します。UTF-16 ではサロゲートペア (2 つの 16 ビットユニット) で表現するため同じく 4 バイトです。UTF-32 は固定長で 1 コードポイント = 4 バイトのため計算は単純ですが、メモリ効率は最も悪くなります。家族絵文字 👨👩👧👦 の場合、UTF-8 で 25 バイトを消費する点は、データベースのカラムサイズ設計で特に注意が必要です。
ZWJ・Variation Selector・サロゲートペアの仕組み
Unicode では、複数のコードポイントを ZWJ (Zero Width Joiner: ゼロ幅接合子、U+200D) で結合して 1 つの絵文字を表現する仕組みがあります。家族の絵文字 👨👩👧👦 は「男性 (U+1F468) + ZWJ + 女性 (U+1F469) + ZWJ + 女の子 (U+1F467) + ZWJ + 男の子 (U+1F466)」の 7 コードポイントで構成されています。
この設計が採用された背景には、絵文字の組み合わせの爆発的な増加があります。肌色 5 種類 × 性別 × 職業を個別に登録すると数万種類が必要になりますが、ZWJ による合成方式なら基本パーツの組み合わせで表現できます。Unicode Consortium はこの方式により、コードポイントの枯渇を防ぎつつ多様性を実現しました。
ZWJ 以外にも、絵文字の表示を制御する不可視のコードポイントが存在します。
- Variation Selector (異体字セレクタ): U+FE0F (絵文字表示) と U+FE0E (テキスト表示) の 2 種類があります。例えば ❤ (U+2764) は、U+FE0F を付けると ❤️ (カラー絵文字)、U+FE0E を付けると ❤︎ (テキスト記号) として表示されます。この不可視文字がバイト数に加算されるため、見た目は同じでもデータサイズが異なるケースが生じます。
- 肌色修飾子 (Skin Tone Modifier): U+1F3FB〜U+1F3FF の 5 段階で、フィッツパトリック・スケール (皮膚科学の肌色分類) に基づいています。修飾子を付けると 1 コードポイント (4 バイト) が追加されます。
- Regional Indicator Symbol (地域指標記号): 国旗絵文字は A〜Z に対応する 26 個の Regional Indicator Symbol (U+1F1E6〜U+1F1FF) を 2 つ組み合わせて表現します。🇯🇵 は U+1F1EF (J) + U+1F1F5 (P) です。ISO 3166-1 の国コードに対応しているため、存在しない組み合わせ (例: U+1F1FF + U+1F1FF) は未定義の表示になります。
JavaScript の内部文字列表現は UTF-16 を採用しているため、BMP 外の絵文字 (U+10000 以上) はサロゲートペアとして 2 つの 16 ビットユニットで表現されます。これが "😀".length が 1 ではなく 2 を返す根本的な理由です。サロゲートペアの上位 (U+D800〜U+DBFF) と下位 (U+DC00〜U+DFFF) を分割してしまうと、不正な文字列が生成されるため、文字列の切り詰め処理では特に注意が必要です。
プラットフォーム別の絵文字カウントと表示差異
- X (Twitter): 絵文字は内部的に一律 2 文字としてカウントされます (日本語テキストの場合は 1 文字)。ZWJ で結合された家族絵文字 👨👩👧👦 も 2 文字扱いです。280 文字制限の中で絵文字を多用する場合、この仕様を把握しておかないと投稿が途中で切れる原因になります。
- Instagram: キャプションの 2,200 文字制限では、絵文字は 1 文字としてカウントされます。ただし、ハッシュタグ内の絵文字は検索対象にならないため、ハッシュタグには絵文字を含めないのが効果的です。
- LINE: テキストメッセージでは絵文字は 1 文字としてカウントされます。ただし LINE 独自のスタンプ絵文字と Unicode 絵文字は内部的に異なる扱いを受けるため、API 経由でメッセージを送信する開発者は注意が必要です。
- Slack: メッセージ本文では絵文字は 1 文字としてカウントされますが、カスタム絵文字はショートコード (例:
:thumbsup:) として扱われ、コロンを含む文字列全体が文字数に加算されます。 - SMS: 絵文字を 1 つでも含めると、エンコーディングが GSM-7 から UCS-2 に切り替わり、1 通あたりの文字数上限が 160 文字から 70 文字に激減します。マーケティング SMS で絵文字を使う場合、送信コストが約 2.3 倍になる可能性があります。
同じ絵文字でも、Apple・Google・Samsung・Microsoft の各プラットフォームでデザインが大きく異なる点にも注意が必要です。例えば 🔫 (ピストル) は Apple がおもちゃの水鉄砲デザインに変更した後、他社も追随しましたが、変更のタイミングにはばらつきがありました。マーケティングや UI デザインで絵文字の見た目に依存する場合は、主要プラットフォームでの表示を事前に確認することを推奨します。
プログラミング言語別の文字数カウントの違い
同じ絵文字でも、プログラミング言語によって length の返す値が異なります。これは各言語の内部文字列表現の違いに起因します。プログラミングの文字列処理に関する技術書でも、この違いは頻出のトピックです。
| 言語 / メソッド | "😀" | "👍🏻" | "👨👩👧👦" | カウント単位 |
|---|---|---|---|---|
JavaScript .length | 2 | 4 | 11 | UTF-16 コードユニット |
JavaScript [...str].length | 1 | 2 | 7 | Unicode コードポイント |
Python 3 len() | 1 | 2 | 7 | Unicode コードポイント |
Rust .len() | 4 | 8 | 25 | UTF-8 バイト数 |
Rust .chars().count() | 1 | 2 | 7 | Unicode コードポイント |
Swift .count | 1 | 1 | 1 | 書記素クラスタ |
Go len() | 4 | 8 | 25 | UTF-8 バイト数 |
Java .length() | 2 | 4 | 11 | UTF-16 コードユニット |
Swift だけが書記素クラスタ単位でカウントするため、どの絵文字でも見た目どおり 1 を返します。JavaScript や Java は UTF-16 ベースのため、BMP 外の絵文字はサロゲートペアで 2 としてカウントされます。Rust と Go はバイト数を返すため、絵文字の文字数カウントには不向きです。開発者は自分が使う言語の length が何を返すのかを正確に把握しておく必要があります。
よくある失敗パターンと対策
- JavaScript の
String.lengthで文字数制限を実装する:"👨👩👧👦".lengthは 11 を返しますが、見た目は 1 文字です。フォーム入力の文字数バリデーションでString.lengthを使うと、絵文字を含む入力が不当に制限されます。Intl.Segmenterを使えば、書記素クラスタ単位で正確にカウントできます。 - 文字列の途中で切り詰めて ZWJ シーケンスを破壊する: 家族絵文字 👨👩👧👦 を途中のバイト位置やコードユニット位置で切り詰めると、ZWJ シーケンスが分断され、個別の人物絵文字がバラバラに表示されたり、不正な文字が出力されたりします。文字列の切り詰めは必ず書記素クラスタ境界で行う必要があります。
- MySQL の
utf8で絵文字を保存しようとする: MySQL のutf8文字セットは最大 3 バイトまでしか対応しておらず、4 バイトの絵文字 (BMP 外のコードポイント) を保存できません。絵文字を扱うにはutf8mb4を指定する必要があります。さらに、VARCHAR(255)のカラムに家族絵文字を大量に含むテキストを保存する場合、UTF-8 で 1 文字あたり最大 25 バイトを消費するため、見た目の文字数よりもはるかに早くカラムサイズの上限に達します。PostgreSQL は最初から UTF-8 の全範囲をサポートしているため、この問題は発生しません。 - 正規表現で絵文字を 1 文字としてマッチさせる: JavaScript の
/./はサロゲートペアの片方だけにマッチします。絵文字を正しくマッチさせるには/./u(Unicode フラグ) を使用し、さらに ZWJ シーケンス全体をマッチさせるには/./v(Unicode Sets フラグ、ES2024) が必要です。 - メール件名に絵文字を多用する: メールクライアントによっては絵文字が正しく表示されず、文字化けや空白になることがあります。特にビジネスメールでは、受信者の環境を考慮して絵文字の使用を控えるのが安全です。
開発者向け: 絵文字を正確にカウントする方法
Intl.Segmenterによる書記素クラスタ分割: ES2022 で導入されたIntl.Segmenterは、書記素クラスタ (ユーザーが「1 文字」と認識する単位) で文字列を分割できます。[...new Intl.Segmenter().segment(str)].lengthで、絵文字を含む文字列の「見た目の文字数」を正確に取得できます。Node.js 16 以降、Chrome 87 以降、Safari 15.4 以降で利用可能です。- 正規表現による絵文字検出と除去: Unicode プロパティエスケープ
/\p{Emoji_Presentation}/uを使えば、文字列中の絵文字を検出・除去できます。ただし\p{Emoji}は数字 (0-9) や # なども含むため、絵文字のみを対象にする場合は\p{Emoji_Presentation}または\p{Extended_Pictographic}を使い分ける必要があります。 - テスト用の絵文字セット: 開発時のテストには、以下の 5 カテゴリを最低限カバーすると主要なエッジケースを網羅できます。(1) 基本絵文字 (😀)、(2) 肌色修飾子付き (👍🏻)、(3) ZWJ シーケンス (👨👩👧👦)、(4) 国旗 (🇯🇵)、(5) キーキャップシーケンス (1️⃣)。
- データベース設計のベストプラクティス: 絵文字を含むテキストを保存するカラムは、見た目の文字数ではなくバイト数ベースでサイズを設計します。MySQL では
utf8mb4を指定し、VARCHARの長さは「想定最大文字数 × 4」を目安に設定します。絵文字を多用するチャットアプリなどではTEXT型の使用も検討してください。
まとめ
絵文字の文字数カウントは見た目ほど単純ではありません。文字数カウントスでは絵文字を含むテキストも正確にカウントできるので、SNS 投稿前の確認にご活用ください。