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

【機能概要】
MySQL utf8mb4
 utf8は過去の歴史的経緯から1文字を表すための文字列長が1~3バイトの時代から1~4バイトになっている。(※1)
 MySQLもutf8は1~3バイトのutf-8しか保存できないため、4バイトになる様な文字コードを格納する場合は、utf8mb4にする必要がある。
 なお、MySQL8.0では、デフォルトはuft8mb4である。

UTF-16サロゲートペア
 UTF-16において2バイトで表現できない文字を4バイトで表すためのコード体系

※1
過去2バイトであったとか、5バイト/6バイト問題は一旦忘れる。
詳しくは、ユニコード戦記とかを読んでもらえれば。。


【本題】
日本語を扱う人にとっては、漢字が間違いなくこの狭間に埋もれてしまう。
そのため、utf8mb4なデータベースにしていても、サロゲートペアな実装してなかったら(Javaは基本lengthで文字数数える実装がどこかに存在するやろから基本アウトやろが)対応ができない。
とはいえ、対応できない文字がどこまで異なるのか?という記載について言及ある記事がなかったので調査を行ってみた。

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では同じ値になっている。
しかし、値の評価方法が異なるため、バイト換算した場合は異なる結果(ex. F0908080[UTF-8の4バイト目先頭] と D800DC00[UTF-16の4バイト目先頭])になる。
では、UTF-16ではUTF-8で扱っている3バイト目の文字はどうなるのか?という点は、2バイトで取り扱える文字の範囲がUTF-16ではu+FFFFとUTF-8の3バイト目の文字範囲をカバーしている。

(参考リンク)
JISX0221:2014 国際符号化文字集合(UCS)
Unicode 13.0 Bookmarks
Shift_JIS, UTF-8 バイトパターン - Qiita
UCS-2とUTF-8
Unicode
Unicode - Compart
Unicode文字ツール
인코딩과 문자 집합/UTF/1 - 위키책
UTF8 4byte 目次

【その他】
・調査したタイミングでは、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

・将来的に利用できるいう意味で、色々なサイトで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

・内部実装も各所で苦労がある模様…

現在の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 暗黒面

Javaの内部エンコーディングがUTF-16BEと記載されているドキュメント
 正確にはUTF-16とまでしか書かれておらず、UTF-16BE[ビック・エンディアン]と書かれている箇所が見つけれなかった。
Charset (Java SE 11 & JDK 11 )

ユニコード戦記 ─文字符号の国際標準化バトル

ユニコード戦記 ─文字符号の国際標準化バトル

  • 作者:小林龍生
  • 発売日: 2011/06/10
  • メディア: 単行本