Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 3 years have passed since last update.
UTF-8のテーブル(MySQL5.6)に竈門禰󠄀豆子が格納できない問題を調べてみた
竈門禰󠄀豆子をMySQL5.6のテーブルにinsertしようとすると正しく格納できず、竈門禰となってしまうケースがあるという話を聞き、調べてみました。
実践
まずは試しにやってみます。
mysql>showcreatetableverification\G***************************1.row***************************Table:verificationCreateTable:CREATETABLE`verification`(`name`varchar(100)COLLATEutf8_binDEFAULTNULL)ENGINE=InnoDBDEFAULTCHARSET=utf8COLLATE=utf8_bin1rowinset(0.01sec)mysql>insertintoverification(name)values('竈門禰󠄀豆子');QueryOK,1rowaffected,2warnings(0.01sec)mysql>select*fromverification;+-----------+|name|+-----------+|竈門禰|+-----------+1rowinset(0.00sec)確かに竈門禰になっていることがわかりました。
調査
文字コード周りの設定を見てみます。
mysql>SHOWVARIABLESLIKE'char%';+--------------------------+----------------------------+|Variable_name|Value|+--------------------------+----------------------------+|character_set_client|utf8||character_set_connection|utf8||character_set_database|utf8||character_set_filesystem|binary||character_set_results|utf8||character_set_server|utf8||character_set_system|utf8||character_sets_dir|/usr/share/mysql/charsets/|+--------------------------+----------------------------+8rowsinset(0.01sec)テーブルの文字コード設定はutf8です。公式リファレンスにも記載がありますが、MySQLのutf8文字セットは、3byteまでの文字を扱うことができます。
mysql>select*fromCHARACTER_SETSwhereCHARACTER_SET_NAMElike'utf8%';+--------------------+----------------------+---------------+--------+|CHARACTER_SET_NAME|DEFAULT_COLLATE_NAME|DESCRIPTION|MAXLEN|+--------------------+----------------------+---------------+--------+|utf8|utf8_general_ci|UTF-8Unicode|3||utf8mb4|utf8mb4_general_ci|UTF-8Unicode|4|+--------------------+----------------------+---------------+--------+2rowsinset(0.00sec)というわけで、文字が途中で途切れて格納されるのは文字コードの設定がutf8であることが一因のようです。
ただ、素直に考えるなら竈門禰で途切れているので豆がutf8で扱えない4byte文字ということになります。こんなメジャーな漢字が4byte文字とは到底思えません。確認してみると、
>>>len('豆'.encode('utf-8'))3やっぱり3byteでした。そうなると疑わしいのは禰󠄀です。
よくよく見てみると、insertしようとした竈門禰󠄀豆子に対し、実際に登録されたのは竈門禰となっていて、偏がネから示に変わっていました。何故こんなことが起きるのでしょうか?
禰󠄀について掘り下げて調べてみます。
>>>len('禰󠄀'.encode('utf-8'))77byte…?テーブル格納後の禰を見てみると…
>>>len('禰'.encode('utf-8'))33byteになっています。insertのタイミングで4byteの何らかの情報が欠落してテーブルに格納されたようです。禰󠄀は一体何者なのか、Unicodeのコードポイントを調べてみます。
>>>ord('禰󠄀')Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>TypeError:ord()expectedacharacter,butstringoflength2found#2文字??>>>s='禰󠄀'>>>list(s)['禰','󠄀']>>>foriinlist(s):...hex(ord(i))...'0x79b0''0xe0100'16進表現で0x79b0と0xe0100の2文字で構成されていることがわかりました。0xe0100を調べてみると、これは異体字セレクタというもののようです。Wikipediaによれば、
異体字セレクタ (英: Variation Selector) は、Unicode および ISO/IEC 10646 (UCS) における、文字の字体をより詳細に指定するためのセレクタ (選択子) である。
とあり、禰を基底文字として偏が異なる文字であることを表現するためのセレクタが含まれていることがわかりました。
まとめ
なぜ竈門禰󠄀豆子がinsert時に一部欠落するかというと、
- utf8のテーブルには4byte文字を格納できない
禰󠄀は禰の異体字であり、禰+ 4byteの異体字セレクタの組み合わせで表現される- したがって、
禰󠄀を格納しようとすると、4byteである異体字セレクタのみ欠落して格納されてしまう
ということでした。異体字セレクタは今まで全く存在を知らず、文字コードの世界は奥が深い、、、と思わされる結果になりました。
追記
この問題を回避したい場合、4byte文字を扱えるutf8mb4を使いましょう。
(参考:https://dev.mysql.com/doc/refman/5.6/ja/charset-unicode-utf8mb4.html )
なお、MySQL8系ではutf8mb4がデフォルトのようです。
(参考:https://dev.mysql.com/doc/refman/8.0/ja/server-system-variables.html#sysvar_character_set_client )
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme