5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

4バイト文字対応の基本と実務ノウハウ

Posted at

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バイト文字への対応は、そのための“ちょっとした備え”として役に立ちます。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?