PHPでのテキストエンコーディングの定義-既存のソリューションの概要ともう1つの自転車

タスクに直面-ページ/テキスト/何かエンコーディングの自動検出。 タスクは新しいものではなく、多くの自転車がすでに発明されています。 記事では、ネットワーク上で見つかったものの簡単な概要に加えて、私自身の申し出が、私にとっては価値のある解決策のようです。



1. mb_detect_encoding()を使用しないのはなぜですか?



つまり、機能しません。



見てみましょう:

//   -     CP1251 $string = iconv('UTF-8', 'Windows-1251', '    ,   ,       ,     .'); // ,    md_detect_encoding().  $strict = FALSE var_dump(mb_detect_encoding($string, array('UTF-8'))); // UTF-8 var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251'))); // Windows-1251 var_dump(mb_detect_encoding($string, array('UTF-8', 'KOI8-R'))); // KOI8-R var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R'))); // FALSE var_dump(mb_detect_encoding($string, array('UTF-8', 'ISO-8859-5'))); // ISO-8859-5 var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R', 'ISO-8859-5'))); // ISO-8859-5 //  $strict = TRUE var_dump(mb_detect_encoding($string, array('UTF-8'), TRUE)); // FALSE var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251'), TRUE)); // FALSE var_dump(mb_detect_encoding($string, array('UTF-8', 'KOI8-R'), TRUE)); // FALSE var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R'), TRUE)); // FALSE var_dump(mb_detect_encoding($string, array('UTF-8', 'ISO-8859-5'), TRUE)); // ISO-8859-5 var_dump(mb_detect_encoding($string, array('UTF-8', 'Windows-1251', 'KOI8-R', 'ISO-8859-5'), TRUE)); // ISO-8859-5
      
      





ご覧のとおり、出力は完全に混乱しています。 関数がこのように動作する理由が明確でない場合はどうしますか? そう、グーグル。 素晴らしい答えを見つけました。



mb_detect_encoding()を使用するすべての期待を最終的に払拭するには、mbstring拡張機能のソースを取得する必要があります。 だから、私たちの袖をまくり、行った:

 // ext/mbstring/mbstring.c:2629 PHP_FUNCTION(mb_detect_encoding) { ... //  2703 ret = mbfl_identify_encoding_name(&string, elist, size, strict); ...
      
      





Ctrl +クリック:

 // ext/mbstring/libmbfl/mbfl/mbfilter.c:643 const char* mbfl_identify_encoding_name(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict) { const mbfl_encoding *encoding; encoding = mbfl_identify_encoding(string, elist, elistsz, strict); ...
      
      





Ctrl +クリック:

 // ext/mbstring/libmbfl/mbfl/mbfilter.c:557 /* * identify encoding */ const mbfl_encoding * mbfl_identify_encoding(mbfl_string *string, enum mbfl_no_encoding *elist, int elistsz, int strict) { ...
      
      





不要なソースで記事が乱雑にならないように、メソッドの全文を掲載しません。 自分の目で見るのが面白い人。 行番号593で調べます。実際には、文字がエンコードに適しているかどうかを確認するためにチェックが行われます。

 // ext/mbstring/libmbfl/mbfl/mbfilter.c:593 (*filter->filter_function)(*p, filter); if (filter->flag) { bad++; }
      
      





シングルバイトキリル文字の主なフィルターは次のとおりです。



Windows-1251(元のコメントが保存されました)

 // ext/mbstring/libmbfl/filters/mbfilter_cp1251.c:142 /* all of this is so ugly now! */ static int mbfl_filt_ident_cp1251(int c, mbfl_identify_filter *filter) { if (c >= 0x80 && c < 0xff) filter->flag = 0; else filter->flag = 1; /* not it */ return c; }
      
      







KOI8-R

 // ext/mbstring/libmbfl/filters/mbfilter_koi8r.c:142 static int mbfl_filt_ident_koi8r(int c, mbfl_identify_filter *filter) { if (c >= 0x80 && c < 0xff) filter->flag = 0; else filter->flag = 1; /* not it */ return c; }
      
      







ISO-8859-5(すべてがここで楽しい)

 // ext/mbstring/libmbfl/mbfl/mbfl_ident.c:248 int mbfl_filt_ident_true(int c, mbfl_identify_filter *filter) { return c; }
      
      





ご覧のとおり、ISO-8859-5は常にTRUEを返します(FALSEを返すには、filter-> flag = 1を設定する必要があります)。



彼らがフィルターを見ると、すべてが適切な場所に落ちました。 KOI8-RのCP1251は、決して区別できません。 ISO-8859-5一般に、エンコードのリストがある場合-常にtrueとして検出されます。



一般的に、失敗します。 理解可能である-文字コードによってのみ、一般にエンコードを認識できません。これらのコードは異なるエンコードで交差するためです。



2. Googleを提供するもの



また、Googleはあらゆる種類の不幸をもたらします。 ここにはソースを投稿しません。必要に応じて参照してください(http://の後にスペースを削除します。リンク付きのテキストを表示する方法はわかりません)。



http:// deer.org.ua/2009/10/06/1/

http:// php.su/forum/topic.php?forum=1&topic=1346



3.ハブを検索する



1)再び文字コード: habrahabr.ru/blogs/php/27378/#comment_710532



2)私の意見では、非常に興味深い解決策: habrahabr.ru/blogs/php/27378/#comment_1399654

リンク上のコメントの長所と短所。 個人的には、このソリューションはエンコーディング検出のためだけに冗長であると信じています-それは非常に強力であることが判明しました。 その中のエンコーディングの定義は副作用です)。



4.実際、私の決定



最後のセクションの2番目のリンクを表示しているときに、アイデアが生まれました。 これは、大きなロシア語のテキストを取得し、さまざまな文字の頻度を測定し、これらの頻度のコーディングを検出するという考え方です。 今後は、大文字と小文字に問題があるとすぐに言います。 したがって、大文字と小文字を区別する場合と区別しない場合の両方で、文字頻度の例を投稿します(「スペクトル」と呼びます)(2番目の場合、同じ頻度でさらに大きな文字を追加しましたが、大きな文字は削除しました)。 これらの「スペクトル」では、頻度が0.001未満でスペースが含まれるすべての文字が切り取られます。 戦争と平和を処理した後に私が得たものは次のとおりです。



大文字と小文字を区別する「スペクトル」:

 array ( '' => 0.095249209893009, '' => 0.06836817536026, '' => 0.067481298384992, '' => 0.055995027400041, '' => 0.052242744063325, .... '' => 0.002252892226507, '' => 0.0021318391371162, '' => 0.0018574762967903, '' => 0.0015961610948418, '' => 0.0014044332975731, '' => 0.0013188987793209, '' => 0.0012623590130186, '' => 0.0011804488387602, '' => 0.001061932790165, )
      
      







大文字と小文字を区別しません:

 array ( '' => 0.095249209893009, '' => 0.095249209893009, '' => 0.06836817536026, '' => 0.06836817536026, '' => 0.067481298384992, '' => 0.067481298384992, '' => 0.055995027400041, '' => 0.055995027400041, .... '' => 0.0029893589260344, '' => 0.0029893589260344, '' => 0.0024649163501406, '' => 0.0024649163501406, '' => 0.002252892226507, '' => 0.002252892226507, '' => 0.0015961610948418, '' => 0.0015961610948418, )
      
      







異なるエンコーディングのスペクトル(配列キー-対応するエンコーディングの対応する文字のコード):



Windows-1251: 大文字と小文字を区別する大文字と小文字を区別しない

KOI8-R: 大文字と小文字を区別する大文字と小文字を区別しない

ISO-8859-5: 大文字と小文字を区別する大文字と小文字を区別しない



次。 未知のエンコーディングのテキストを取得し、チェックする各エンコーディングについて、現在の文字の頻度を見つけ、このエンコーディングの「評価」に追加します。 より高い評価のエンコーディングは、ほとんどの場合、テキストのエンコーディングです。



 $encodings = array( 'cp1251' => require 'specter_cp1251.php', 'koi8r' => require 'specter_koi8r.php', 'iso88595' => require 'specter_iso88595.php' ); $enc_rates = array(); for ($i = 0; $i < len($str); ++$i) { foreach ($encodings as $encoding => $char_specter) { $enc_rates[$encoding] += $char_specter[ord($str[$i])]; } } var_dump($enc_rates);
      
      





自宅でこのコードを実行しようとしないでください-それは動作しません。 あなたはそれを擬似コードと考えることができます-私は記事を煩雑にしないために詳細を省略しました。 $ char_specterは、pastebinによって参照される配列そのものです。



結果


表の行—テキストエンコーディング、列— $ enc_rates配列の内容。



1)$ str = 'ロシア語のテキスト';

cp1251 | koi8r | iso88595 |

0.441 | 0.020 | 0.085 | Windows-1251

0.049 | 0.441 | 0.166 | KOI8-R

0.133 | 0.092 | 0.441 | ISO-8859-5







すべてが素晴らしいです。 実際のエンコーディングは、すでに他のエンコーディングよりも4倍高い評価を持っています-これは短いテキストです。 長いテキストでは、比率はほぼ同じになります。



2)$ str = 'LINE CAPSOMロシア語テキスト';

cp1251 | koi8r | iso88595 |

0.013 | 0.705 | 0.331 | Windows-1251

0.649 | 0.013 | 0.201 | KOI8-R

0.007 | 0.392 | 0.013 | ISO-8859-5









おっと! フルポリッジ。 CP1251の大文字は通常KOI8-Rの小文字に対応しているためです。 そして、小さな文字は大きな文字よりもはるかに頻繁に使用されます。 したがって、CP1251のラインキャップをKOI8-Rとして定義します。

大文字と小文字を区別しないようにします(「スペクトル」大文字と小文字を区別しない)



1)$ str = 'ロシア語のテキスト';

cp1251 | koi8r | iso88595 |

0.477 | 0.342 | 0.085 | Windows-1251

0.315 | 0.477 | 0.207 | KOI8-R

0.216 | 0.321 | 0.477 | ISO-8859-5









2)$ str = 'LINE CAPSOMロシア語テキスト';

cp1251 | koi8r | iso88595 |

1.074 | 0.705 | 0.465 | Windows-1251

0.649 | 1.074 | 0.201 | KOI8-R

0.331 | 0.392 | 1.074 | ISO-8859-5









ご覧のとおり、大文字と小文字を区別する「スペクトル」(行に少数の大文字が含まれる場合)と大文字と小文字を区別しない場合の両方で、正しいエンコーディングが安定して導かれます。 大文字と小文字を区別しない2番目のケースでは、リーダーはもちろんそれほど自信がありませんが、小さな回線でも非常に安定しています。 文字の重みをいじることができます-たとえば、頻度に対して非線形にします。



5.結論



このトピックでは、UTF-8での作業は考慮されていません。基本的な違いはありません。ただし、文字コードの取得と文字列の文字への分割は、少し長く/複雑になります。

これらのアイデアは、キリル文字エンコーディングだけでなく、もちろん拡張できます-質問は、対応する言語/エンコーディングの「スペクトル」のみです。



PSそれが非常に必要/興味深い場合-私はGitHubに完全に動作するライブラリの2番目の部分を投稿します。 投稿のデータはそのようなライブラリを自分のニーズにすばやく書き込むのに十分だと思いますが、ロシア語の「スペクトル」はレイアウトされていますが、必要なすべてのエンコーディングに簡単に転送できます。



更新済み

素晴らしい機能がコメントをすり抜けました。これは、「squalor」列の下に投稿したリンクです。 たぶん彼はその言葉に興奮しましたが、それを公開して以来、彼はそれを公開しました-彼はそのようなものを編集することに慣れていません。 根拠にならないように、著者と言われるように、100%動作するかどうかを見てみましょう。

1)この機能の「通常の」操作中にエラーが発生しますか? コンテンツが100%有効であるとします。

答え:はい、彼らはそうします。

2)UTF-8および非UTF-8以外のものを定義しますか?

答え:いいえ、決定しません。



コードは次のとおりです。

 $str_cp1251 = iconv('UTF-8', 'Windows-1251', ' '); var_dump(md5($str_cp1251)); var_dump(md5(iconv('Windows-1251', 'Windows-1251', $str_cp1251))); var_dump(md5(iconv('KOI8-R', 'KOI8-R', $str_cp1251))); var_dump(md5(iconv('ISO-8859-5', 'ISO-8859-5', $str_cp1251))); var_dump(md5(iconv('UTF-8', 'UTF-8', $str_cp1251)));
      
      





出力は何ですか:

 m00t@m00t:~/workspace/test$ php detect_encoding.php string(32) "96e14d7add82668414ffbc498fcf2a4e" string(32) "96e14d7add82668414ffbc498fcf2a4e" string(32) "96e14d7add82668414ffbc498fcf2a4e" string(32) "96e14d7add82668414ffbc498fcf2a4e" PHP Notice: iconv(): Detected an illegal character in input string in /home/m00t/workspace/test/detect_encoding.php on line 36 PHP Stack trace: PHP 1. {main}() /home/m00t/workspace/test/detect_encoding.php:0 PHP 2. iconv() /home/m00t/workspace/test/detect_encoding.php:36 string(32) "d41d8cd98f00b204e9800998ecf8427e"
      
      





何が見えますか? iconv($エンコード、$ encodigng)の後のシングルバイトのキリル文字は変更されません。 UTF-8のみが非UTF-8と区別できます。 そして-ワーニングを犠牲にして。

私見、PHPが「愚か者のための言語」と考えられているのはまさにこのようなコードの部分です(c) -この言語に関するトピックでトロルを書くことができない方法。



All Articles