テキスト ファイルのエンコード/コードページを検出するにはどうすればよいですか

StackOverflow https://stackoverflow.com/questions/90838

質問

私たちのアプリケーションでは、テキスト ファイル (.txt, .csv, 、など)さまざまな情報源から。これらのファイルは異なるコードページまたは未知のコードページで作成されたため、読み取り時にガベージが含まれる場合があります。

テキスト ファイルのコードページを (自動的に) 検出する方法はありますか?

detectEncodingFromByteOrderMarks, 、上で StreamReader コンストラクター、のために働く UTF8 および他の Unicode マーク付きファイルですが、次のようなコードページを検出する方法を探しています。 ibm850, windows1252.


ご回答ありがとうございます。これが私がやったことです。

私たちが受け取るファイルはエンドユーザーからのものであり、彼らはコードページについてはまったく知りません。受信者もエンドユーザーであり、現時点で受信者がコードページについて知っていることは次のとおりです。コードページが存在しますが、煩わしいです。

解決:

  • 受信したファイルをメモ帳で開き、文字化けしている部分を確認します。誰かがフランソワか何かという名前なら、あなたの人間の知性があればそれを推測できるでしょう。
  • 私は、ユーザーがファイルを開いて、正しいコードページが使用されている場合にファイルに表示されることをユーザーが知っているテキストを入力するために使用できる小さなアプリを作成しました。
  • すべてのコードページをループし、ユーザーが指定したテキストを含む解決策を提供するコードページを表示します。
  • 複数のコードページがポップアップ表示される場合は、ユーザーにさらにテキストを指定するように依頼します。
役に立ちましたか?

解決

コードページは検出できないので、それを知らせる必要があります。バイトを分析して推測することはできますが、奇妙な (時には面白い) 結果が得られる可能性があります。今は見つかりませんが、メモ帳をだまして英語のテキストを中国語で表示できるはずです。

とにかく、これはあなたが読む必要があるものです:すべてのソフトウェア開発者が絶対に、積極的に Unicode と文字セットについて知っておくべき絶対最低限の事項 (言い訳はできません!).

具体的にジョエルはこう言います。

エンコーディングに関する最も重要な事実

今説明したことをすべて忘れてしまった場合は、非常に重要な事実を 1 つ思い出してください。どのようなエンコーディングが使用されているかを知らずに文字列を取得しても意味がありません。もう、頭を突っ込んで「プレーン」テキストが ASCII であるかのように振る舞うことはできません。プレーンテキストなどというものはありません。

メモリ内、ファイル内、または電子メール メッセージ内に文字列がある場合、その文字列がどのようなエンコーディングで使用されているかを知る必要があります。そうしないと、文字列を解釈したり、ユーザーに正しく表示したりすることができません。

他のヒント

非 UTF エンコーディング (つまり、BOM なし) の場合、基本的にはテキストのヒューリスティック分析と統計分析を行うことになります。を見てみるとよいでしょう。 ユニバーサル文字セット検出に関する Mozilla の論文 (同じリンクですが、Wayback Machine 経由でより適切なフォーマットが施されています).

やってみました Mozilla Universal Charset Detector 用の C# ポート

例から http://code.google.com/p/ude/

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}    

コードページを検出できません

これは明らかに誤りです。すべての Web ブラウザには、エンコーディングの兆候がまったくないページを処理するための、ある種の汎用文字セット検出機能が備わっています。Firefox にはそれがあります。コードをダウンロードして、それがどのように実行されるかを確認できます。いくつかのドキュメントを参照してください ここ. 。基本的に、これはヒューリスティックですが、非常にうまく機能します。

適度な量のテキストがあれば、言語を検出することも可能です。

もう一つあります Google を使用して次のことを見つけました。

この質問には非常に遅れており、この解決策は(英語中心の偏見と統計的/実証的テストの欠如のため)一部の人にとっては魅力的ではないことはわかっていますが、特にアップロードされた CSV データを処理する場合には、私にとっては非常にうまく機能しました。

http://www.architectshack.com/TextFileEncodingDetector.ashx

利点:

  • BOM検出機能内蔵
  • デフォルト/フォールバックエンコーディングをカスタマイズ可能
  • (私の経験では) UTF-8 と Latin-1 スタイルのファイルが混在するいくつかのエキゾチックなデータ (フランス語の名前など) を含む西ヨーロッパベースのファイル (基本的に米国と西ヨーロッパの環境の大部分) ではかなり信頼できます。

注記:このクラスを作成したのは私なので、当然、話半分に聞いてください。:)

メモ帳++ にはすぐに使えるこの機能があります。変更にも対応しております。

別の解決策を探していたところ、

https://code.google.com/p/ude/

このソリューションはちょっと重いです。

最初の 4 バイトとおそらく XML 文字セット検出に基づく基本的なエンコード検出が必要でした。そのため、インターネットからサンプル ソース コードをいくつか取得し、わずかに修正したバージョンを追加しました。

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

Java 用に書かれています。

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

ファイルからおそらく最初の 1024 バイトを読み取るだけで十分ですが、ファイル全体をロードしています。

93.9% の解決策を探している人がいる場合。これは私にとってはうまくいきます:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}

Python でも同様のことを行ったことがあります。基本的に、さまざまなエンコーディングからの大量のサンプル データが必要です。これらのデータは、スライドする 2 バイト ウィンドウによって分割され、エンコーディングのリストの値を提供するバイト ペアをキーとして辞書 (ハッシュ) に保存されます。

その辞書 (ハッシュ) を指定して、入力テキストを取得し、次のようにします。

  • BOM 文字 (UTF-16-BE の場合は '\xfe\xff'、UTF-16-LE の場合は '\xff\xfe'、UTF-8 の場合は '\xef\xbb\xbf' など) で始まる場合は、提案どおりに扱います
  • そうでない場合は、テキストの十分な大きさのサンプルを取得し、サンプルのすべてのバイトペアを取得して、辞書から提案された最も一般的ではないエンコーディングを選択します。

UTF エンコードされたテキストもサンプリングした場合は、 ない 任意の BOM から開始すると、2 番目のステップで、最初のステップから漏れた BOM がカバーされます。

これまでのところ、私にとってはうまく機能しており (サンプル データと後続の入力データにはさまざまな言語の字幕が付いています)、エラー率は減少しています。

StreamReader クラスのコンストラクターは、「エンコーディングの検出」パラメーターを受け取ります。

「uchardet」ツールは、各文字セットの文字頻度分布モデルを使用してこれをうまく実行します。ファイルが大きく、「典型的な」ファイルほど信頼性が高くなります (当然のことです)。

ubuntu では、ただ apt-get install uchardet.

他のシステムでは、ソース、使用法、ドキュメントをここから入手してください。 https://github.com/BYVoid/uchardet

C ライブラリにリンクできる場合は、次を使用できます。 libenca. 。見る http://cihar.com/software/enca/. 。マニュアルページから:

ENCAは、指定されたテキストファイル、または標準入力が与えられていない場合に標準の入力を読み取り、その言語に関する知識(お客様にサポートする必要があります)と解析、統計分析、推測、ブラックマジックの混合物を使用して、エンコーディングを決定します。

GPL v2 です。

同じ問題が発生しましたが、それを自動的に検出するための適切な解決策がまだ見つかりませんでした。今はそのために PsPad (www.pspad.com) を使用しています ;) 正常に動作します

これは基本的にヒューリスティックに帰着するため、同じソースから以前に受信したファイルのエンコードを最初のヒントとして使用すると役立つ場合があります。

ほとんどの人 (またはアプリケーション) は、毎回ほぼ同じ順序で、多くの場合同じマシン上で処理を実行します。そのため、Bob が .csv ファイルを作成して Mary に送信するときは、常に Windows-1252 または彼のマシンのデフォルトが何であっても。

可能であれば、少しの顧客トレーニングも害にはなりません:-)

実は、ファイルのエンコーディングを検出するための、プログラミングではなく一般的な方法を探していたのですが、まだ見つかりませんでした。さまざまなエンコーディングでテストしたところ、テキストは UTF-7 であることがわかりました。

それで、私が最初にやっていたことは次のとおりです。StreamReader ファイル = File.OpenText(フルファイル名);

それを次のように変更する必要がありました。StreamReader ファイル = 新しい StreamReader(フルファイル名, System.Text.Encoding.UTF7);

OpenText は UTF-8 であると想定します。

また、この新しいStreamReader(FullFileName、True)のようなStreamReaderを作成することもできます。これは、ファイルのByteOrdermarkからエンコードを検出しようとする必要があることを意味しますが、私の場合は機能しませんでした。

AkelPadでファイルを開き(または文字化けしたテキストをコピー/ペーストして)、「編集」->「選択」->「再コード...」に移動します。→「自動検出」にチェックを入れます。

ITmeze 投稿のアドオンとして、この関数を使用して C# ポートの出力を Mozilla Universal Charset Detector 用に変換しました。

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN

ありがとう @エリック・アロネスティ 言及するために uchardet.

一方、Linux 用にも (同じ?) ツールが存在します。 chardet.
または、cygwin では次のように使用できます。 chardetect.

見る: シャルデのマニュアルページ: https://www.commandlinux.com/man-page/man1/chardetect.1.html

これにより、指定された各ファイルの文字エンコーディングがヒューリスティックに検出 (推測) され、各ファイルの検出された文字エンコーディングの名前と信頼レベルがレポートされます。

この質問がされてから 10 年 (!) が経過しましたが、MS の優れた非 GPL ソリューションについてはまだ言及されていません。 I多言語2 API。

すでに言及したほとんどのライブラリは Mozilla の UDE に基づいており、ブラウザーがすでに同様の問題に取り組んでいることは合理的だと思われます。Chrome の解決策が何なのかはわかりませんが、IE 5.0 以降、MS は次のような解決策をリリースしました。

  1. GPL などのライセンスの問題がなく、
  2. おそらく永久にバックアップされ、維持されます。
  3. 豊富な出力 - エンコード/コードページのすべての有効な候補と信頼スコアを提供します。
  4. 使い方は驚くほど簡単です (単一の関数呼び出しです)。

ネイティブCOM呼び出しですが、 とても素敵な作品があります Carsten Zeumer によるもので、.net 使用のための相互運用の混乱を処理します。他にもいくつかありますが、概して、このライブラリはそれにふさわしい注目を集めていません。

このコードを使用して、ファイルを読み取るときに Unicode と Windows のデフォルトの ANSI コードページを検出します。他のコーディングの場合は、手動またはプログラミングによる内容のチェックが必要です。これは、テキストを開いたときと同じエンコーディングでテキストを保存するために使用できます。(VB.NETを使用しています)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top