MySQLのutf8mb4とUTF-16のサロゲートペア

機能概要

MySQL utf8mb4

utf-8は過去の歴史的経緯から1文字を表すための文字列長が1~3バイトの時代から1~4バイトになっている。(※1)
MySQL5.7までの'utf8'はutf8mb3のエイリアスで、1~3バイトのutf-8しか保存できない。
4バイト以上を格納する場合はutf8mb4を文字コードに設定する必要がある。
なお、MySQL8.0では、デフォルトはuft8mb4である。
そのため、utf8と定義するとutf8mb4に紐づくが、utf8mb4と書く方が安全かと思う。

※1
過去一律3バイトであったとか、5・6バイト問題はここでは割愛する。
詳しく知りたい人は、ユニコード戦記とかを読んでもらえれば。。

UTF-16サロゲートペア

UTF-16において2バイトで表現できない文字を4バイトで表すためのコード体系のこと。
なお、後述もするが、UTF-8にもサロゲートペア文字は存在する。

調査したかったこと

utf-16で取り扱うサロゲートペア文字になる範囲と、utf-8mb4で取り扱うサロゲートペア文字な範囲が実はずれがないのか気になり調査した。

utf-8の各バイト単位の下限と上限値
バイト数 下限 上限 unicode下限 unicode上限
1バイト 00 7F U+0000 U+007F
2バイト C280 DFBF U+0080 U+07FF
3バイト E0A080 EFBFBF U+0800 U+FFFF
4バイト F0908080 F48FBFBF U+10000 U+10FFFF
utf-16の通常文字とサロゲート文字の下限と上限値
バイト数 下限 上限 unicode下限 unicode上限
2バイト 0000 FFFF U+0000 U+FFFF
4バイト D800DC00 DBFFDFFF U+10000 U+10FFFF

結果、utf8mb4はutf-8の4バイト目を扱うための文字コードである事が分かった。
(4バイト目の値が、unicodeでは同じ文字範囲(u+10000~u+10FFFF)を指すため、UTF-8及びUTF-16では同じ値になっていることが分かる)

また、サロゲートペア文字でない箇所において、utf-8では1~3バイトの可変長で表現している。
一方、utf-16では一律2バイトで表現しているため、上記の様な値の違いが発生する。

補記事項

UTF-8で許されていない文字コードの値

調査したタイミングでは、UTF-8に関して、以下が許されてない模様である。

  • 2バイト表現:下限値の第一オクテットがC0~C1
  • 4バイト表現:上限値の第一オクテットがF4~F7

3バイト文字までがUnicodeでは、第0面と呼び、4バイト文字は現在第1面~第16面として定義されている。
但し、第4面~第13面は現時点では未定義である。
https://ja.wikipedia.org/w/index.php?title=Unicode§ion=10#%E9%9D%A2

unicodeで表現できる最大値

将来的に利用できるいう意味で、色々なサイトでu+110000~u+1FFFFFもunicodeの最大値として紹介しているサイトを見かける。
しかし、現時点ではu+10FFFFがunicodeの最大値である。
まぁ、いずれ将来は使いきることもあるであろうから、言葉のあや程度で考えておけば良い。

The Unicode Standard supports three character encoding forms: UTF-32, UTF-16, and UTF-8. Each encoding form maps the Unicode code points U+0000..U+D7FF and U+E000..U+10FFFF to unique code unit sequences.

(Unicode CONSORTIUM Unicode 13.0.0 3.9 Unicode Encoding Forms より抜粋)
http://www.unicode.org/versions/Unicode13.0.0/ch03.pdf

Javaの内部エンコーディングがUTF-16BEと記載されているドキュメント

正確にはUTF-16とまでしか書かれておらず、UTF-16BE[ビック・エンディアン]と書かれている箇所が見つけれなかった。
Charset (Java SE 11 & JDK 11 )

UCS-4

ざっくり言うとサロゲートペア文字のこと
UCS-2BMPでU+0000~U+FFFFの文字のこと
なお、UCS-2とUCS-4はもうオワコンな言い方(※2)

※2
RFC3629 12. Changes from RFC 2279 より

Straightened out terminology.
UTF-8 now described in terms of an encoding form of the character number.
UCS-2 and UCS-4 almost disappeared.

RFC 3629 - UTF-8, a transformation format of ISO 10646 日本語訳

通信用語の基礎知識 UCS-2 UnicodeUCS-2 より

ISO/IEC 10646誕生の経緯から、Unicodeに配慮してこの「UCS-2」という用語が生まれたが、Unicode用語としては既に廃止されている。

UCS-2 ‐ 通信用語の基礎知識

Oracle Databaseにおけるutf-8対応

AL32UTF8というのを利用する必要があるのだが、以下の様な経緯があるため。

現在のOracle Databaseでも、CESU-8を「UTF8」として、「普通のUTF-8」を「AL32UTF8」として扱っているため注意を要する。
MySQLでも「utf8」を指定した場合は4オクテット列が扱えず、CESU-8相当の符号化を必要とする
(4オクテット列対応のUTF-8は「utf8mb4」として別途定義されているが、MySQL 5.5.3以降でないと使用できない[9])。

(wikipedia UTF-8 サロゲートペアの扱い より抜粋)
UTF-8 - Wikipedia

CESU-8

UTF-8黒歴史の一つっぽい。
Unicode 暗黒面

サロゲートペア文字への向き合い方

日本語を扱う人にとっては、漢字が間違いなくサロゲートペア文字問題が付きまとう。
最近なら絵文字(※3)も影響してくる。

なお、データベースでutf8mb4管理できても、アプリ側でサロゲートペア対応できてないと片手落ちである。
例えば、Javaはlengthメソッドで文字数を数える実装がどこかに存在する。
lengthメソッドはサロゲートペア文字に対応していないメソッドのため、基本その記述があるJavaアプリはアウトである。
そのため、Javaの世界だけで考えるとサロゲートペア文字をまともに扱えるシステムはまだまだ少数と思う。
また、それらも踏まえたうえで、どう取り扱うのか決定しないといけない。

※3
絵文字もサロゲートペア文字で表現しないと駄目な絵文字と、サロゲートペア文字で無くても使える絵文字がある。
そのため、絵文字扱える=サロゲートペア対応されていると考えるのは短絡的なので注意する必要がある。
yoneyore.hatenablog.com