PHPで見た目が同じ違う漢字に立ち向かう
- 掲載日
経緯
あるシンプルな検索機能(SQLで LIKE '%キーワード%'としているような本当に単純な検索)で、タイトルに「市民」と入っているにも関わらず、「市民」で検索しても引っかからない記事があるというお問い合わせを受けました。

解析ツールサイトを使って文字コードを調査したところ、どうも「民」の文字コードが異なっており、通常の「民」のUnicodeは「E6 B0 91」となるところを、「E2 BA A0」となっておりました。
CJK部首補助というもので、本来部首を表すための漢字とのこと。
漢字の解説などで使用するケース以外ではほぼほぼ使用しないと思われます。
元の入力が誤っていなくても、PDF等から漢字をコピーペーストするとこうなることがあるようです。
そして少なくとも自分の構築したサイトで使用しているフォント上はまったく同じ形で出力されてしまうため、ブラウザ上では判別できず非常に厄介。

添付のようにさくらエディタに張り付ければ見た目の違いも分かります。
解決
文字コードやフォントについて詳しくないですが、こういったものは最初の問合せのような「確かにその文字が入っている(ように見える)のに検索に引っかからない」事態にならないためにも、全て一般的な漢字に変換したいです。
PHPにはNormalizerというクラスがあり、文字列の正規化が可能です。
正規化とは、文字や文字列を正式に決められた表現に変換する処理のことです。 これは、検索や並べ替えなどの際に比較をするときに特に重要となります。 ただ、それだけではなく、テキストを保存する際に表記を統一するために用いることもあります。
Normalizer クラスより引用
とのことなので、PHPで以下の処理を挟んであげれば問題ないのでは?と思いましたがそうは問屋が卸さない。
$normalized = Normalizer::normalize($str, Normalizer::FORM_C);以下のような確認用のスクリプトを作って実行したところ、直っておらず。
<?php
// 部首補助置換テスト
$bad = "⺠";
$normalized = Normalizer::normalize($bad, Normalizer::FORM_C);
echo sprintf("→になってて欲しい: 民\n");
echo sprintf("%s => %s\n", $bad, $normalized);
echo sprintf("%s \n", $bad === $normalized ? "直ってない" : "直った");

調べたところ、「部首文字と統合漢字は別扱い」とのこと。難しい…。
統合漢字ではない(別物扱いなので、正規化のマップが無い)ため、こういったものは手動で変換ツールを作るか、有志のライブラリを使用するしかないようです。
今回はこちらのサイトの一覧を使用させて頂いて、自前で変換する処理を作りました。
class CJK
{
const CJK_MAP = [
"\u{2E83}" => "\u{4E5A}",
"\u{2E85}" => "\u{4EBB}",
"\u{2E87}" => "\u{20628}",
~ (中略) ~
"\u{2FD3}" => "\u{9F8D}",
"\u{2FD4}" => "\u{9F9C}",
"\u{2FD5}" => "\u{9FA0}"
];
public static function convert($text)
{
return strtr($text, self::CJK_MAP);
}
}
以下のように呼び出して使用できます。
$result = CJK::convert("民");これを確認用スクリプトで以下のように差し替えて実行します。
require_once __DIR__ . '/cjk.php';
// 部首補助置換テスト
$bad = "⺠";
$normalized = CJK::convert($bad);
echo sprintf("→になってて欲しい: 民\n");
echo sprintf("%s => %s\n", $bad, $normalized);
echo sprintf("%s \n", $bad === $normalized ? "直ってない" : "直った");添付スクリーンショットの通り普通の漢字に直ります。

まとめ
- 確かにその文字があるはずなのに検索に引っ掛からない場合、文字コード変換して一般的な文字コードになっているか確認。
- Normalize等で変換できるなら変換する。
- 駄目なら有志ライブラリや自前の変換スクリプトを作成して置き換えを行う。
参考リンク
- やっかいな漢字 – CJK部首補助/康煕部首
「CJK部首補助または康煕部首を解決する一覧」を使わせて頂きました。 - リアルタイム文字コード変換/解析ツール

この記事を書いた人
- A.W.
- 茨城県在住Webエンジニアです。 PHP、PostgreSQL、Linuxなどを業務で使用しています。 趣味ではGoやNuxt、Flutterをやってます。
