絵文字

Unicode に収録された絵文字記号。テキストコミュニケーションで感情や概念を視覚的に表現する。

絵文字 (Emoji) は Unicode に収録された絵文字記号で、テキストコミュニケーションにおいて感情や概念を視覚的に表現するために使われます。起源は日本の携帯電話向けに作られた記号セットで、確認できる最も早い例は 1997 年の J-フォン (後の SoftBank) 端末に搭載された 90 種です。広く知られているのは 1999 年に NTT ドコモの i-mode 向けに作られた 176 種のセットで、その後 2010 年 10 月の Unicode 6.0 で 722 種が国際標準に採用されました。2026 年時点の絵文字仕様 (Emoji 17.0) で数えられる絵文字は 3,953 で、改訂ごとに追加が続いています。

絵文字の内部構造は見た目以上に複雑です。多くの絵文字は基本多言語面 (BMP) の外に配置されているため、UTF-16 ではサロゲートペアとして 2 コードユニットを消費します。さらに、肌の色を変える修飾子 (Emoji Modifier)、性別を指定する ZWJ (ゼロ幅接合子) シーケンス、国旗を表す Regional Indicator Symbol など、複数のコードポイントを組み合わせて 1 つの絵文字を構成する仕組みが多数存在します。たとえば「👨‍👩‍👧‍👦」(家族) は 7 つのコードポイント (4 人の人物 + 3 つの ZWJ) で構成されており、UTF-16 では 11 コードユニット、UTF-8 では 25 バイトになります。ただし「絵文字はすべて BMP の外」というわけではありません。✅ (U+2705) や ☺ (U+263A) のような記号由来の絵文字は BMP 内にあるため、サロゲートペアにならず UTF-16 では 1 コードユニットです。絵文字を特別扱いするコードを書くときは、この 2 系統が混在する前提で組む必要があります。

エンコーディングごとのバイト数も異なります。UTF-8 では BMP 外の絵文字が 1 つあたり 4 バイト、前述の記号由来の絵文字は 3 バイト、肌の色の修飾子や国旗のように 2 コードポイントを組み合わせるものは 8 バイト、ZWJ シーケンスを含む複合絵文字はさらに増えます。データベースの VARCHAR 列に絵文字を格納する場合、MySQL では utf8mb4 エンコーディングが必要です (utf8 は 3 バイトまでしか対応しないため絵文字を格納できません)。この制約を知らずにデータベースを設計すると、絵文字を含むデータの保存時にエラーが発生する典型的な落とし穴です。

SNS の文字数制限における絵文字の扱いはプラットフォームによって異なります。X (旧 Twitter) では絵文字 1 つを 2 文字としてカウントします。SMS では絵文字を 1 つ含めるだけでメッセージ全体が Unicode (UCS-2) エンコーディングに切り替わるため、1 通あたりの上限が 160 文字から 70 文字に減ります。さらに BMP 外の絵文字は UCS-2 で 2 単位を占めるため、絵文字だけを並べた場合に 1 通へ収まるのは 35 個程度です。ここで実務的な判断基準になるのは「絵文字 1 つの追加コストが 1 文字分では済まない」という点で、上限ぎりぎりの文面に絵文字を足すと、想定より早く分割送信 (追加課金) が発生します。

プログラミングにおける絵文字の扱いには注意が必要です。JavaScript の .length プロパティはサロゲートペアを 2 としてカウントするため、絵文字 1 つが 2 以上の長さを返すことがあります。ここで Array.from() を使えば解決すると説明されることがありますが、これは正確ではありません。Array.from() はコードポイント単位に分割するだけなので、実際に試すと家族の絵文字は 7、肌の色を指定した「👍🏽」と日本の国旗「🇯🇵」はいずれも 2 を返します。見た目どおりの 1 を得られるのは書記素クラスタ単位で区切る Intl.Segmenter のほうで、new Intl.Segmenter('ja', { granularity: 'grapheme' }) で分割してから数えます。.length は過大、Array.from() は「サロゲートペアだけは吸収する中間の値」と理解しておくと、どの数え方を使うべきかの判断を誤りません。

文字数カウントの観点では、絵文字は「1 文字に見えて複数文字」の代表例です。コードポイント数、UTF-16 コードユニット数、バイト数、書記素クラスタ数のいずれでカウントするかによって結果が大きく異なります。文字数カウントツールを実装する際には、ユーザーが期待する「見た目の 1 文字」と内部表現のギャップを適切に処理することが求められます。

この記事を共有