システム開発でチェックすべき文字列🧑‍🤝‍🧑

## はじめに

システム開発において、ユーザーが文字列を入力できるような機能を設計・実装する場合、「文字列が正常に保存・表示されるか」は非常に重要なチェックポイントです。
特に、**文字コードの扱いや絵文字（emoji）などの特殊文字**は、システムに思わぬバグを引き起こすことがあります。

この記事では、実際の開発現場で「チェックすべき文字列」の具体例を紹介し、その理由や注意点を解説します。

---

## チェックすべき文字列の例

以下に挙げる文字列は、実際にテストケースとして入力・保存・表示が正常に行えるかを確認することをおすすめします。

```:チェックすべき文字列（手入力せず必ずコピーして貼り付けて下さい）
𠮷野家
aaa🧑‍🤝‍🧑aaa
aaa🧑‍🤝‍🧑
🧑‍🤝‍🧑aaa
🧑‍🤝‍🧑
aaa😄aaa
aaa😄
😄aaa
😄
```

### 各文字列のチェックポイント

#### 「[𠮷野家](https://x.com/yoshinoyagyudon/status/1597354664114241536)」
 - 一見普通の日本語ですが、過去に「吉」の文字が旧字体の「𠮷（U+20BB7）」で入力されているケースがあり、**一部のシステムでは正しく保存・表示できない**という問題が報告されています。
 - UTF-8では「𠮷」は**4バイト文字**であり、MySQLなどで文字セットが `utf8` のままだと保存に失敗する可能性があります（`utf8mb4`を使用すべき）（吉は3バイト文字）。

#### 絵文字（🧑‍🤝‍🧑など）
 - 「🧑‍🤝‍🧑（People Holding Hands）」のような**ゼロ幅接合子（ZWJ）**を用いた複合絵文字は、1文字でありながら**18バイト以上の長さを持つ**ことがあります。
 - 絵文字が含まれる場合、以下の観点でチェックが必要です：
 - **保存可否（DBエンコーディング）**
 - **バリデーション（文字数カウント vs バイト長）**
 - **表示崩れ（フォントが対応しているか）**

#### 「😄（Smile Emoji）」
 - 通常の単体絵文字ですが、旧システムや古いフォントでは化けてしまうことがあります。
 - OSやブラウザ、フォント依存のため、**複数環境での表示確認**が大切です。
---

## その他の考慮点：ドキュメントや外部連携
絵文字を含むテキストを、メモ帳で開く事ができるフォーマットで作成した場合、メモ帳で開くと意図しない文字化けやフォーマット崩れが発生することがあります。 
以下の記事では、そういった問題に関する注意喚起や対策方法が紹介されています：

📎 [絵文字1️⃣2️⃣3️⃣4️⃣ を含むドキュメントファイルを作成する場合の注意点（Qiita）](https://qiita.com/uni928/items/6b945b6db260cd20b0ae)

外部システムとの連携（LINEやSlack APIなど）では、絵文字がエスケープされる／されない等の仕様の違いもあるため、文字列の送信・受信での挙動もチェック対象です。

---

## 技術的背景：4バイト文字とUTF-8
特に注意すべきは、**UTF-8で4バイト以上になる文字の扱い**です。
 - 一般的なMySQLの `utf8` は**3バイトまで**の文字しか保存できません。
 - 絵文字や一部の漢字、特殊記号を正しく扱うには、**`utf8mb4`** を使用する必要があります。
 - アプリケーション側の文字数カウントロジックも、「**バイト数**」**ではなく**「**Unicodeコードポイント**」**ベース**にすることが重要です。

---

## 絵文字の抹殺について
絵文字が原因で、文字列の後半が消えてしまう現象が発生する場合には、対象の邪悪な絵文字を抹殺する必要があるかもしれません。（絵文字や 4 バイト以上の文字が入力されている場合、エラーメッセージを出すなど）

新しい絵文字が追加されるたびに対処が必要になるため、いたちごっこになる可能性もありますが、必要に応じて以下のようなコードで対応できます。

```php:対応コード
$targetString = 'Hello🧑‍🤝‍🧑World';
$targetString = $this->removeEmojis($targetString);
$targetString = $this->remove4ByteChars($targetString);
```

```php:実装例
// 絵文字を除去
private function removeEmojis(string $string): string {
    $regex = '/[\x{1F1E6}-\x{1F1FF}' .  // 国旗（Regional Indicator Symbols）
              '\x{1F300}-\x{1F5FF}' .  // 絵文字や記号
              '\x{1F600}-\x{1F64F}' .  // 顔文字
              '\x{1F680}-\x{1F6FF}' .  // 乗り物、地図記号
              '\x{1F700}-\x{1F77F}' .  // 錬金術記号
              '\x{1F780}-\x{1F7FF}' .  // 幾何学模様など
              '\x{1F800}-\x{1F8FF}' .  // 矢印記号など
              '\x{1F900}-\x{1F9FF}' .  // 顔文字や物
              '\x{1FA00}-\x{1FA6F}' .  // 拡張絵文字（チェスなど）
              '\x{1FA70}-\x{1FAFF}' .  // 追加の顔文字
              '\x{2600}-\x{26FF}' .    // 雑多な記号（天気など）
              '\x{2700}-\x{27BF}' .    // 装飾記号や矢印
              '\x{FE00}-\x{FE0F}' .    // 異体字セレクタ
              '\x{200D}' .             // ゼロ幅結合子（ZWJ）
            ']/u';

    return preg_replace($regex, '', $string);
}

// 4バイト文字（Unicodeサロゲートペアなど）を除去
private function remove4ByteChars(string $string): string {
    return preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $string);
}
```

### 補足

removeEmojis() は、主に人が目視で認識できる絵文字の削除を意図していますが、新しく追加された絵文字を完全にカバーすることは難しいため、運用上のバランスを考慮して使用してください。

remove4ByteChars() は、MySQLなどの一部データベースが4バイト文字に対応していない場合に特に有用です。（この関数はすべての4バイト文字を除去しますが、ゼロ幅結合子（ZWJ）などの2バイト文字は削除されないため、「🧑‍🤝‍🧑」のような複合絵文字の残骸（ZWJ）が残ることがあります。removeEmojis と合わせて使用する必要があり、結果的にいたちごっこである事に変わりはありません）

---

## まとめ

本記事では、システム開発における「チェックすべき文字列」について、以下の観点で解説しました：
 - 通常の文字列にも潜む文字コード問題（例：𠮷野家）
 - 複合絵文字の保存・表示・カウントの落とし穴
 - 外部連携・ドキュメント生成での文字化けリスク
 - UTF-8エンコーディングとデータベースの注意点

特に「🧑‍🤝‍🧑」のような合成絵文字は、禁則処理との相性が悪い（合成絵文字の途中で不自然に改行されるなど）ため、禁則処理を実装している場合には事前に対応を検討する必要がありそうです。

この記事を参考に、バグの早期発見につながれば大変嬉しく思います。
特に **MySQL** で `utf8mb4` を未使用の方は、今すぐ確認することを**強くおすすめ**します。

最後までご覧いただき、ありがとうございました！

***

## 追記

この記事は ChatGPT で添削しています。
生成 AI による添削が苦手な方は申し訳ありません。