PHP:元の文字セットを知らなくても、文字列をUTF-8に変換するか、少なくとも試してみる


Answers

祖国のロシアには4つの人気のあるエンコーディングがあるので、あなたの質問はここで大きな需要です。

コードページが交差するので、シンボルのcharコードによってのみエンコーディングを検出することはできません。 異なる言語のコードページには、完全な交差点もあります。 だから、 別のアプローチが必要です。

未知のエンコーディングで作業する唯一の方法は、確率で作業することです。 だから、「このテキストのエンコーディングは何ですか?」という質問に答えることは望ましくない。「 このテキストをエンコードする可能性が最も高いのは何か 」を理解しようとしている。

ここで人気のあるロシアのテクニカルブログの一人がこのアプローチを発明しました:

サポートしたいすべてのエンコーディングで、charコードの確率範囲を構築します。 あなたはあなたの言語でいくつかの大きなテキストを使ってそれを構築することができます(例えば、いくつかのフィクション、英語のためのシェイクスピア、ロシアのためのトルストイ、笑)。 あなたはこのようなスムースを得るでしょう:

    encoding_1:
    190 => 0.095249209893009,
    222 => 0.095249209893009,
    ...
    encoding_2:
    239 => 0.095249209893009,
    207 => 0.095249209893009,
    ...
    encoding_N:
    charcode => probabilty

次。 あなたは未知のエンコーディングでテキストを取り、 "確率辞書"のすべてのエンコーディングで、未知のエンコーディングされたテキストのすべてのシンボルの頻度を検索します。 シンボルの合計確率。 評価の高いエンコーディングが勝者になる可能性があります。 より大きいテキストのためのよりよい結果。

あなたが興味を持っているなら 、私は喜んでこの仕事であなたを助けることができます。 我々は、2つのコードの確率リストを構築することによって、精度を大幅に向上させることができる。

Btw。 mb_detect_encodingは動作しません。 はい、まったくありません。 "ext / mbstring / libmbfl / mbfl / mbfl_ident.c"にあるmb_detect_encodingソースコードを見てください。

Question

私は世界中のクライアントを扱うアプリケーションを持っています。当然データベースに入るすべてのものがUTF-8でエンコードされていることを望みます。

私の主な問題は、文字列のソースコードがどのようなものになるのかわからないことです。テキストボックス( <form accept-charset="utf-8">を使って)は、ユーザーは実際にフォームを提出します)、アップロードされたテキストファイルからのものである可能性があります。

私が必要とするのは、可能な限りUTF-8エンコードされたものが自分のデータベースに入っていることを確認する関数またはクラスです。 私はiconv(mb_detect_encoding($text), "UTF-8", $text);を試しましたiconv(mb_detect_encoding($text), "UTF-8", $text); しかしそれには問題があります(入力が 'fiancée'の場合は 'fianc'を返します)。 私は多くのことを試しました= /

ファイルのアップロードでは、エンドユーザーに使用するエンコーディングを指定し、出力がどのように表示されるかのプレビューを表示するように求めるのが好きですが、これは厄介なハッカーに対しては役に立ちません(実際には、もう少し簡単です)。

私はこの件に関して他のSOの質問を読んだことがありますが、「RSSフィードを解析する必要があります」や「ウェブサイトからデータを取得する」などの微妙な違いがあるようです(実際は「できません」)。

しかし、少なくとも良い試みがある何かがなければなりません!




どのエンコーディングが使用されているかを推測しようとする一連のメトリックを設定できます。 繰り返しますが、完璧ではありませんが、mb_detect_encoding()のミスの一部を捕まえることができます。




本当に良い答えがあり、ここであなたの質問に答えようとしています。 私はエンコーディングのマスターではありませんが、私は純粋な UTF-8スタックをデータベースに完全に埋め込むというあなたの希望を理解しています。 私はテーブル、フィールド、および接続にMySQLのutf8mb4エンコーディングを使用しています。

私の状況は、「私は自分のサニタイザー、バリデーター、ビジネスロジック、そしてHTMLフォームや電子メール登録リンクからのデータの場合、UTF-8を扱う準備が整ったステートメントを必要としています。 だから私の簡単なやり方で、私はこの考え方から始めました。

  1. エンコーディングを検出しようとしました: $encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];
  2. エンコーディングを検出できない場合は、 throw new RuntimeExceptionます。
  3. 入力がUTF-8ならば、続けてください。
  4. それ以外の場合は、 ISO-8859-1またはASCII

    a。 UTF-8への変換を試みる(完了していない)

    b。 変換された値のエンコーディングを検出する

    c。 報告されたエンコーディングと変換された値が両方ともUTF-8である場合は、実行してください。

    d。 そうでなければ、 throw new RuntimeException

私の抽象クラスSanitizer

    private function isUTF8($encoding, $value)
    {
        return (($encoding === 'UTF-8') && (utf8_encode(utf8_decode($value)) === $value));
    }

    private function utf8tify(&$value)
    {
        $encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];

        mb_internal_encoding('UTF-8');
        mb_substitute_character(0xfffd); //REPLACEMENT CHARACTER
        mb_detect_order($encodings);

        $stringEncoding = mb_detect_encoding($value, $encodings, true);

        if (!$stringEncoding) {
            $value = null;
            throw new \RuntimeException("Unable to identify character encoding in sanitizer.");
        }

        if ($this->isUTF8($stringEncoding, $value)) {
            return;
        } else {
            $value = mb_convert_encoding($value, 'UTF-8', $stringEncoding);
            $stringEncoding = mb_detect_encoding($value, $encodings, true);

            if ($this->isUTF8($stringEncoding, $value)) {
                return;
            } else {
                $value = null;
                throw new \RuntimeException("Unable to convert character encoding from ISO-8859-1, or ASCII, to UTF-8 in Sanitizer.");
            }
        }

        return;
    }

私は抽象的なSanitizerクラスからエンコーディングに関する懸念Sanitizer 、単にSanitizerの具体的な子インスタンスにEncoderオブジェクトを挿入する必要があるという主張をすることができます。 しかし、私のアプローチの主な問題は、それ以上の知識がなければ、私が望んでいないタイプのエンコーディングを拒否することです(PHP mb_ *関数に依存しています)。 それ以上の研究がなければ、それが人口の一部を傷つけるかどうかを知ることはできません(または、重要な情報を失う場合)。 だから私はもっと学ぶ必要があります。 私はこの記事を見つけました。

テキストを扱うエンコーディングと文字セットについて、すべてのプログラマーが絶対に知っておく必要があること

さらに、暗号化されたデータが( OpenSSLまたはmcryptを使用して)自分の電子メール登録リンクに追加されるとどうなりますか? これがデコードに干渉することはありますか? Windows-1252はどうですか? セキュリティの影響はどうですか? Sanitizer::isUTF8でのutf8_decode()utf8_encode()の使用は疑わしいです。

PHP mb_ *関数の不具合を指摘しています。 私はiconvを調べるのに時間はかかりませんでしたが、それがmb_ *関数よりもうまくいくなら、私に教えてください。




完全に正確な文字列の文字セットを識別する方法はありません。 文字セットを推測しようとする方法があります。 これらの方法の1つ、おそらく/現在PHPで最高のものは、mb_detect_encoding()です。 これはあなたの文字列をスキャンし、特定の文字セットに固有のものの出現を探します。 あなたの文字列によっては、そのような区別可能な出現がないかもしれません。

ISO-8859-1文字セットvs ISO-8859-15( http://en.wikipedia.org/wiki/ISO/IEC_8859-15#Changes_from_ISO-8859-1

少数の異なる文字があり、それを悪化させるために、同じバイトで表されます。 バイト0xA4が文字列中の¤または€を表すはずかどうかを検出する方法はありません。エンコーディングがわからなくても文字列が与えられるため、正確な文字セットを知る方法はありません。

(注:人間の要素や、さらに高度なスキャン技術(Oroboros102が示唆するものなど)を追加したり、周囲の状況に基づいて把握しようとしたりすることができます。あまりにも遠い)

たとえば、UTF-8とISO-8859-1のように区別できる違いがあるので、あなたが確信しているときにそれを理解しようとする価値はありますが、それに頼ることはできません。

興味深い読書: http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string : http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string

しかし、正しい文字セットを保証する他の方法があります。 フォームに関しては、可能な限りUTF-8を適用してみてください(あなたの投稿がすべてのブラウザでUTF-8になることを確認するために、雪だるまをチェックしてください: http://intertwingly.net/blog/2010/07/29/Rails-and-Snowmen : http://intertwingly.net/blog/2010/07/29/Rails-and-Snowmen )少なくとも、あなたのフォームを通して投稿されたすべてのテキストがutf_8であることを確認できます。 アップロードされたファイルに関しては、ファイルのBOMを使って検出を助けるために、可能であればexec()を使ってUnixの 'file -i'コマンドを実行してみてください。データのスクラップに関して、HTTPヘッダー、通常は文字セットを指定します。 XMLファイルを解析するときに、XMLメタデータに文字セット定義が含まれているかどうかを確認してください。

自動的に文字セットを推測しようとするのではなく、可能であれば、あなた自身で特定の文字セットを確保するか、検出に頼る前に取得しているソースから定義を取得しようとしてください(該当する場合)。




あなたの質問はかなり答えられているようですが、私はあなたのケースを簡素化する方法があります:

mysqlから文字列データを返そうとしても同様の問題がありました。データベースとPHPの両方をutf-8にフォーマットされた文字列を返すように設定していました。 私はエラーを得た唯一の方法は実際にデータベースからそれらを返すことでした。

最後に、ウェブを介してセーリングをすると、それを処理する本当に簡単な方法が見つかりました。

あなたはあなたのmysqlのさまざまなフォーマットと照合でこれらのタイプの文字列データをすべて保存することができます。あなたの必要なことは、あなたのPHP接続ファイルで、照合順序をutf-8に設定することです。

$connection = new mysqli($server, $user, $pass, $db);
$connection->set_charset("utf8");

これは、最初にデータを任意の形式または照合順序で保存し、PHPファイルに戻すときにのみ変換することを意味します。

それが助けてくれることを願って!




Links