Into: もう一つの不都合な真実
未知のテキストのエンコーディングを 100% の精度および/または信頼度で検出することは不可能です。
実際には、考えられる結果の範囲全体にケースがあります。UTF-8 の多言語テキストがそのように正しく検出されることはほぼ確実ですが、ISO-8859 エンコーディングのファミリーのどれを検出することは完全に不可能です。いくつかのテキストに対応します -- 統計分析を行うつもりがない限り、経験に基づいた推測を行うことさえできません!
私たちは何を扱う必要がありますか?
それでは、何ができるか見てみましょう。まず第一に、カスタム ツールを戦闘に持ち込まない限り、mb_detect_encoding
できることは限られています。残念ながら、それだけではありません。姉妹関数のドキュメントには次のようにmb_detect_order
記載されています。
mbstring は現在、次のエンコード検出フィルターを実装しています。以下のエンコーディングに無効なバイト シーケンスがある場合、エンコーディングの検出は失敗します。
UTF-8、UTF-7、ASCII、EUC-JP、SJIS、eucJP-win、SJIS-win、JIS、ISO-2022-JP。
ISO-8859-X の場合、mbstring は常に ISO-8859-X として検出します。
UTF-16、UTF-32、UCS2、および UCS4 の場合、エンコードの検出は常に失敗します。
したがって、日本語のエンコーディングを無視すると、基本的に UTF-8、UTF-7、および ASCII を区別する機能があります。ISO-8859-X を検出することはできません。これを考慮すると、すべてのテキストがこれらのエンコーディングのいずれかとして「認識」されるためです (つまり、100% の誤検知率が発生します -- 良くありません)。また、UTF を含むグループ-16 は単にサポートされていません。
残念ながら、悪いニュースはそれだけではありません。エンコーディングの順序も重要です。UTF-7 または ASCII でエンコードされたテキストも有効な UTF-8 であるため、候補リストの先頭に UTF-8 を配置すると、これが唯一の結果になることが保証されます。そのため、絶対に避ける必要があります。 .
デフォルトの検出順序は php.ini の設定に依存するため、これに依存せず、独自の検出順序を設定して既知の状態に移行する必要があります。
mb_detect_order('ASCII, UTF-8'); // I left UTF-7 out, but who cares?
少なくとも、テキストが ASCII か UTF-8 かはわかりますよね? うーん、ダメ。「UTF-8」と言うとき、あなたが本当にそれを意味することを特に要求しない限り、そうではありません:
$valid_utf8 = "\xC2\xA2";
$invalid_utf8 = "\xC2\x00";
mb_detect_order('UTF-8');
echo mb_detect_encoding($valid_utf8); // "utf-8": correct
echo mb_detect_encoding($invalid_utf8); // "utf-8": WTF?!?!?!
上記の問題はtrue
、$strict
パラメーターを渡さない限り、UTF-8 の検出が...少し楽観的すぎることです。
さて、これで実際に何ができるでしょうか?
これは、エンコーディングを検出する正しい方法です (ここでは、複数形を使用し続けることがほとんどできていません)。
$valid_utf8 = "\xC2\xA2";
$invalid_utf8 = "\xC2\x00";
$ascii = "hello world";
mb_detect_order('ASCII, UTF-8');
echo mb_detect_encoding($valid_utf8, mb_detect_order(), true); // OK: "utf-8"
echo mb_detect_encoding($invalid_utf8, mb_detect_order(), true); // OK: false
echo mb_detect_encoding($ascii, mb_detect_order(), true); // OK: "ascii"
有効な UTF-8 ではないテキストで何ができますか?
そのテキストに関する帯域外の情報がない限り、残念ながら何もありません。
OK、それは完全に真実ではありません。実際に実行できることがいくつかあります。
- テキストの先頭に BOM があるかどうかを確認します。おそらく存在しないでしょうし、たとえ数学的に存在したとしても、1 バイトのエンコーディングを Unicode と間違えるかもしれませんが、試してみる価値はあります。
- UTF-16 のフレーバーかどうかを確認します。偶数バイトの大部分が同じ値である場合は、UTF-16 LE を見ている可能性があります。これが奇数バイトの大部分で発生する場合は、UTF-16 BE を見ている可能性があります。残念ながら、どちらの場合も確実ではありません。
- テキストが ISO-8859-X であると仮定し、このエンコーディングに対応するスクリプトの既知のプロパティに基づいて統計分析を行い、結果が予想に近いかどうかを確認します。このクラスの一部のエンコーディングでは十分に近く、他のエンコーディングではかなり離れている場合は、経験に基づいた推測を行うことができます。