Webエンジニアとして

絵文字を文字数カウントするとおかしくなる問題の謎を解き明かす

文字カウントするときに厄介な絵文字が存在します。例えばこのような「👩‍👩‍👧‍👧」絵文字です。1文字に見えますよね?

でもこれ7文字換算されることもあります。

果たしてどういう仕組みになってるのでしょうか?見てみましょう。

スポンサーリンク

まずは普通の文字をカウントする

まずは試しでテーブルを作ってみる。

create table user (
  user_message VARCHAR(50) not null
);

NG: カタカナ51文字(3×51 = 153バイト)

カタカナを挿入してみよう。日本語文字は3バイト。

カタカナ51文字(3×51 = 153バイト)を挿入するinsert文を用意して実行してみる。

insert into user(
user_message
)
values(
'カナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘイカナヘ'
);

結果

Data too long for column 'user_message' at row 1

バイト数は余裕があるが文字数がオーバーしてるので当然エラーが出る。

絵文字をカウントする

NG: 絵文字「🥺」51文字(4×51 = 201バイト)

絵文字「🥺」を挿入してみよう。

1文字4バイトの絵文字を51文字(204バイト)のINSERT文を用意してみた。

insert into user(
user_message
)
values(
'🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺🥺'
);

Data too long for column 'user_message' at row 1

同様にエラーが出る。バイト数も文字数もオーバーしているので当然。

NG: 1文字に見える絵文字8個(56文字 200バイト)

では1文字に見える絵文字「👩‍👩‍👧‍👧」はどうなるか。

insert into user(
user_message
)
values(
'👩‍👩‍👧‍👧👩‍👩‍👧‍👧👩‍👩‍👧‍👧👩‍👩‍👧‍👧👩‍👩‍👧‍👧👩‍👩‍👧‍👧👩‍👩‍👧‍👧👩‍👩‍👧‍👧'
);

Data too long for column 'user_message' at row 1

これでエラーが出る。8個に見えるがUTF-8だと「👩‍👩‍👧‍👧」が1個で7文字換算で56文字なので文字数オーバーしている。

文字数と聞くと惑わされてしまいそうだ。この絵文字はなんで7文字として計算されるのか?

1文字に見える絵文字「👩‍👩‍👧‍👧」 の文字数をカウントする

カウントがおかしくなる絵文字

今回のデータベースはutf8mb4なので文字数はUTF-8で数えることになる。

人間が識別する単位の数え方ではないことに注意。

まずはじめに「👩‍👩‍👧‍👧」は「woman + woman + girl + girl」が結合した絵文字である。

つまり「👩 + 👩 + 👧 + 👧」ということ。

 

なるほど、じゃあ絵文字4個だから4文字?・・・・とはならない。

ゼロ幅接合子 での結合されている絵文字の正体

最初に説明した「結合した絵文字」であることを考える。

どういうことか、というと結合するのも「文字」で行っていて「ゼロ幅接合子」というもので行っている。

これはUTF-8(16進数)で表現すると「e2808d」になる。

つまり、「👩‍👩‍👧‍👧」という絵文字は

「👩 e2808d 👩 e2808d 👧 e2808d 👧」

という感じで絵文字の結合がおこなれている。

絵文字とゼロ幅接合子の数をカウントすると7個となる。だから7文字になる。

絵文字をUTF-8で分解してみる

最後に絵文字もUTF-8(16進数)で分解してみよう

  • 👩 -> f09f91a9
  • e2808d
  • 👩 -> f09f91a9
  • e2808d
  • 👧 -> f09f91a7
  • e2808d
  • 👧 -> f09f91a7

UTF-8で文字数をカウントするということは上記のような形で区切って数えることになる。

全部まとめると「f09f91a9e2808f09f91a9e2808df09f91a7e2808df09f91a7」こうなる。

 

なんでこの英数字の羅列と絵文字が対応しているの?というとUnicodeコードポイントで定義されているからである。

絵文字をUnicodeコードポイントで表してみると?

「👩‍👩‍👧‍👧」はUnicodeコードポイントで表すと

  1. U+1F469
  2. U+200D
  3. U+1F469
  4. U+200D
  5. U+1F467
  6. U+200D
  7. U+1F467

の7個の集まりである。

絵文字をUnicodeエスケースプシーケンスで表してみると?

これを扱いやすくするために「👩‍👩‍👧‍👧」をUnicodeエスケースプシーケンスにすると

  1. \ud83d\udc69
  2. \u200d
  3. \ud83d\udc69
  4. \u200d
  5. \ud83d\udc67
  6. \u200d
  7. \ud83d\udc67

こうなったりする

絵文字をUTF-8(16進数)で表してみると?

これは上記で書いたのと同じだが「👩‍👩‍👧‍👧」をUTF-8(16進数)にすると

  1. f09f91a9
  2. e2808d
  3. f09f91a9
  4. e2808d
  5. f09f91a7
  6. e2808d
  7. f09f91a7

UTF-8は「Unicode Transformation Format – 8-bit」の略であるようにUnicodeを符号化したものである。

つまりそれぞれ使いやすいように変換してるだけ。

ここらへんは他の記事でも書いたので興味があればどうぞ

絵文字を正しくカウントするってなんだ?絵文字を扱う入力フォームのシステム設計を考える
入力フォームを作る時にこいつの文字数の扱いに苦しみませんか?そう「絵文字」です。 絵文字の文字数をどうやって「正確に」数えてアプリケーションを作るか、という問題...

まとめ

人間が見てる1文字はシステムの世界では1文字でないかもしれない。

「UTF-8で文字数を数える」というのはUnicodeのコードポイント単位で数えること。

環境

mysql: 8.0.31
Collation: utf8mb4_unicode_ci

コメント

タイトルとURLをコピーしました