技術関係

データベースで絵文字の「文字数」ってどう数えてるの?コードポイント?Unicode?となった時に見るページ

さて、MySQLのようなデータベースのカラム型定義の何気なく指定しているVARCHAR(X)は何文字入るのか?

例えばデータベースの文字コードがUTF-8(utf8mb4)だと括弧内の数字は文字数を表し1文字4バイトとして計算する。

つまりVARCHAR(50)は

  • 50文字
  • 200バイト

を許容し、どちらもオーバーすることは許されない。

バイト数は分かった。けど文字数って何を基準に数えるんだろう?絵文字はどう数えるんだろう?

気になるので調べてみましょう。

 

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

create table user (
  user_message VARCHAR(50) not null
);
スポンサーリンク
スポンサーリンク

環境

mariadb: 10.2
Collation: utf8mb4_unicode_ci

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文字として計算されるのか?

👩‍👩‍👧‍👧 をUTF-8で文字数を数える

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

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

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

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

 

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

最初に言った結合した絵文字であることを考える。

どういうことか、というと結合するのも文字で行っている。

結合は「ゼロ幅接合子」というもので行っている。これはUTF-8(16進数)で表現すると「e2808d」になる。

つまり、

「👩 e2808d 👩 e2808d 👧 e2808d 👧」

ということ。

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

 

最後に絵文字も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を符号化したものである。

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

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

絵文字の文字数を「正しく」数えるようにするシステム設計を考えた話「UTF-8?コードポイント?書記素クラスタ?」
要件 500文字を入力出来るフォームがあり、ライブバリデーションで文字カウントを行っている。 ある日ユーザからクレームが入る。 「フォームに絵文字「👩‍👩‍👧‍...

まとめ

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

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

コメント

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