4バイト文字対応の基本と実務ノウハウ
絵文字・異体字・一部の漢字(𠮷 など)によりシステムが落ちる問題を、まとめた記事となります。
1. 4バイト文字(サロゲートペア)とは?
「UTF-16 の 2 ワード(4 バイト)で表現される文字」のこと。
具体例:
- 絵文字 😀
- 旧字体・異体字(例:𠮷)
- Unicode 補助平面(U+10000 以上)の文字
2. なぜ障害の原因になるのか?
4バイト文字はシステムによって扱いがバラバラだからです。
| 要素 | 何が起きるか |
|---|---|
| DB(MySQL utf8) | 3バイトまで → INSERT 時にエラー、文字化け |
| ログ出力 | Java の char(UTF‑16)で正しく扱えず落ちるケース |
| JSON API | 送受信でエラー(エスケープ問題) |
| バリデーション | 「1文字」のつもりが2コードユニット扱い |
3. 実務でよく見るトラブル例
❌ 例1:MySQL に INSERT して落ちる
Incorrect string value: '\xF0\x9F\x98\x80' for column ...
❌ 例2:ログ出力で例外
MalformedInputException: Input length = 1
❌ 例3:外部API に POST したら 400
JSON エンコード時に surrogate pair が破損。
4. データベース対策(最重要)
✔ MySQL / MariaDB は「utf8mb4」を使う
NG:utf8
OK:utf8mb4
変更例
ALTER TABLE users MODIFY name VARCHAR(255) CHARACTER SET utf8mb4;
ALTER DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
5. アプリケーション言語ごとの注意点
Java
-
charは UTF‑16 の 1 ワード(BMP しか 1 文字で扱えない) - サロゲートペアは
codePoint系の API を使う
例:文字数カウント(正しい方法)
int length = name.codePointCount(0, name.length());
PHP
-
strlen()はバイト数 → NG -
mb_strlen()かIntlCharを使用
$len = mb_strlen($name, 'UTF-8');
JavaScript
- JS の
lengthは UTF‑16 のコードユニット数なので誤る - ES2015 から
Array.from()を使う
Array.from(name).length;
6. 禁止文字(4バイト文字)チェック
JavaScript のサンプル
function contains4byteChar(str) {
for (const ch of str) {
if (ch.codePointAt(0) > 0xFFFF) {
return true;
}
}
return false;
}
Java のサンプル
public static boolean has4ByteChar(String text) {
return text.codePoints().anyMatch(cp -> cp > 0xFFFF);
}
PHP のサンプル
function has4ByteChar($str) {
foreach (preg_split('//u', $str, -1, PREG_SPLIT_NO_EMPTY) as $ch) {
if (IntlChar::ord($ch) > 0xFFFF) return true;
}
return false;
}
7. 実務での具体例(安全な書き換え済み)
❌ ユーザーが次の名前を入力して障害発生
「𠮷田商店」
(= 4 バイト文字「𠮷」を含む)
DB(utf8) → INSERT 失敗
Incorrect string value ...
API → 400 Bad Request
外部サービスがサロゲートペア非対応の JSON パーサーを使用。
8. 正しい実装例(フロー)
[受信]
→ 4バイト文字チェック
→ NG:エラー応答
→ OK:DB 保存(utf8mb4)
→ 外部 API 送信
→ 必要なら Unicode エスケープ
9. 4バイト文字対策チェックリスト
DB
utf8mb4 を使用しているか
テーブル・カラムも utf8mb4 か
照合順序は utf8mb4_unicode_ci か
入力
4バイト文字を許可するか不許可にするかを決めているか
不許可の場合、検出ルールをコードポイントで実装しているか
言語仕様
文字数は codePoint ベースで計算しているか
UTF‑16 ベースの length, char を過信していないか
ログ
ログ出力先の文字コードは UTF‑8 か
ログ加工処理でサロゲートペアを壊していないか
API
外部 API の 4 バイト対応状況を確認しているか
JSON シリアライズでサロゲートペアが正しく扱われているか
テスト
絵文字(😀)、異体字(𠮷)、補助平面文字をテストケースに含めているか
API 入出力・DB 保存・ログ出力まで E2E テストできているか
10. 実務での推奨方針まとめ
- 原則:utf8mb4 を前提にする
- 4バイト文字は許可する方向を推奨
- 禁止する場合は正しいコードポイントチェックが必須
- 入力 → DB → API → ログの一貫性を担保する
11. まとめ
4バイト文字のトラブルは特別なことではなく、実務ではよく起きる “あるある” です。
でも、対策ポイントさえ押さえておけば、ほとんど防げます。
どんな文字が来ても壊れないようにしておくことが大事です。
4バイト文字への対応は、そのための“ちょっとした備え”として役に立ちます。