ファイルのエンコーディングを見つける効果的な方法


115

はい、最も頻繁に聞かれる質問です。この問題は私にとって漠然としていて、私はそれについてあまり知りません。

しかし、私はファイルのエンコーディングを見つけるための非常に正確な方法が欲しいです。Notepad ++と同じくらい正確です。



どのエンコーディング?UTF-8 vs UTF-16、ビッグvsリトルエンディアン?または、Shift-JISやキリル文字などの古いMSDosコードページを参照していますか?
— dthorpe

別の可能性のある重複:stackoverflow.com/questions/436220/...
— オデッド

@Oded:Quote "getEncoding()メソッドは、ストリーム用に設定された(JavaDocを読み取る)エンコーディングを返します。エンコーディングは推測されません。"
— ファビオ・アントゥネス

2
いくつかのバックグラウンドリーディングについては、joelonsoftware.com / articles / Unicode.htmlをお読みください。テキストについて知っておくべきことが1つあれば、それはプレーンテキストなど存在しないということです。
— Martijn、2015年

回答:


155

StreamReader.CurrentEncodingプロパティはほとんど私のためにエンコードする正しいテキストファイルを返しません。バイトオーダーマーク(BOM)を分析することで、ファイルのエンディアンを決定することに大きな成功を収めました。ファイルにBOMがない場合、これはファイルのエンコードを判別できません。

* UTF-32LE検出を含み、UTF-32BEの正しいエンコーディングを返すように2020年4月8日更新

/// <summary>
/// Determines a text file's encoding by analyzing its byte order mark (BOM).
/// Defaults to ASCII when detection of the text file's endianness fails.
/// </summary>
/// <param name="filename">The text file to analyze.</param>
/// <returns>The detected encoding.</returns>
public static Encoding GetEncoding(string filename)
{
    // Read the BOM
    var bom = new byte[4];
    using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
    {
        file.Read(bom, 0, 4);
    }

    // Analyze the BOM
    if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
    if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
    if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0 && bom[3] == 0) return Encoding.UTF32; //UTF-32LE
    if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
    if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
    if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return new UTF32Encoding(true, true);  //UTF-32BE

    // We actually have no idea what the encoding is if we reach this point, so
    // you may wish to return null instead of defaulting to ASCII
    return Encoding.ASCII;
}

3
+1。これは私にとっても機能しました(detectEncodingFromByteOrderMarksは機能しませんでした)。ファイルは読み取り専用であるため、IOExceptionを回避するために、「new FileStream(filename、FileMode.Open、FileAccess.Read)」を使用しました。
— Polyfun 14

56
UTF-8ファイルにはBOMがなくてもかまいません。この場合、ASCIIが誤って返されます。
— user626528

3
この答えは間違っています。の参照ソースを見るとStreamReader、その実装はより多くの人々が望むものです。それらは既存のEncoding.Unicodeオブジェクトを使用するのではなく新しいエンコーディングを作成するので、等価性チェックは失敗します(たとえば、Encoding.UTF8異なるオブジェクトを返すことができるため、これはめったに発生しない可能性があります)が、(1)本当に奇妙なUTF-7形式を使用していません。 (2)BOMが見つからない場合はデフォルトでUTF-8になり、(3)別のデフォルトエンコーディングを使用するようにオーバーライドできます。
— 格納庫2015

2
新しいStreamReader(filename、true).CurrentEncoding
— Benoit

4
コードに根本的なエラーがあります。ビッグエンディアン UTF32 署名(00 00 FE FF)を検出すると、システム提供のを返しますEncoding.UTF32。これは、リトルエンディアンエンコーディングです(ここで説明します)。また、@ Nyergudsによって指摘されているように、署名があるFF FE 00 00(en.wikipedia.org/wiki/Byte_order_markによる)UTF32LEはまだ探していません。そのユーザーが指摘したように、それは包括的であるため、2バイトのチェックの前にそのチェックを行う必要があります。
— Glenn Slayden 2018

44

次のコードは、StreamReaderクラスを使用して私にとってうまくいきます:

  using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
  {
      reader.Peek(); // you need this!
      var encoding = reader.CurrentEncoding;
  }

トリックは、Peek呼び出しを使用することです。それ以外の場合、.NETは何もしていません(そして、プリアンブルBOMを読み取っていません)。もちろん、ReadXXXエンコーディングをチェックする前に他の呼び出しを使用する場合、それも機能します。

ファイルにBOMがない場合は、defaultEncodingIfNoBomエンコーディングが使用されます。このオーバーロードメソッドのないStreamReaderもあります(この場合、デフォルト(ANSI)エンコーディングがdefaultEncodingIfNoBomとして使用されます)が、コンテキストでデフォルトエンコーディングと見なすものを定義することをお勧めします。

UTF8、UTF16 / Unicode(LE&BE)およびUTF32(LE&BE)のBOMを含むファイルを使用してこれを正常にテストしました。UTF7では機能しません。


デフォルトのエンコーディングとして設定されたものを返します。私はmomethingが欠けていませんか?
— Ram

1
@DRAM-これは、ファイルにBOMがない場合に発生する可能性があります
— Simon

@Simon Mourierに感謝します。私のpdf /ファイルにはbomがないと思います。このリンクstackoverflow.com/questions/4520184/…は、bomなしで検出しようとするユーザーに役立つ場合があります。
— Ram

1
powershellでは、$ reader.close()を実行する必要がありました。そうしないと、書き込みがロックされていました。 foreach($filename in $args) { $reader = [System.IO.StreamReader]::new($filename, [System.Text.Encoding]::default,$true); $peek = $reader.Peek(); $reader.currentencoding | select bodyname,encodingname; $reader.close() }
— js2010

1
ファイルのエンコーディングがある場合@SimonMourierこれがない仕事をするUTF-8 without BOM
— オズカン

11

次の手順を試します。

1)バイトオーダーマークがあるかどうかを確認します。

2)ファイルが有効なUTF8かどうかを確認します

3)ローカルの「ANSI」コードページを使用します(Microsoftが定義するANSI)

ステップ2が機能するのは、UTF8以外のコードページのほとんどの非ASCIIシーケンスが有効なUTF8ではないためです。


他の答えは私にはうまくいかないので、これはより正しい答えのようです。ファイルの最初の数バイトをFile.OpenReadと.Read-ingすることでそれを行うことができます。
— user420667 2013

1
ステップ2は、ビットパターンをチェックするためのプログラミング作業全体です。
— Nyerguds

1
しかし、デコードが実際に例外をスローするか、または認識されないシーケンスを単に「?」に置き換えるかどうかはわかりません。とにかく、ビットパターンチェックのクラスを作成しました。
— Nyerguds

3
のインスタンスを作成するときにUtf8Encoding、例外をスローするか、サイレントデータ破損を好むかを決定する追加のパラメーターを渡すことができます。
— CodesInChaos 2016年

1
私はこの答えが好きです。ほとんどのエンコーディング(99%のユースケースなど)は、UTF-8またはANSI(Windowsコードページ1252)になります。文字列に置換文字(0xFFFD)が含まれているかどうかを確認して、エンコードが失敗したかどうかを判断できます。
— マーズ

10

これをチェックして。

UDE

これはMozilla Universal Charset Detectorの移植版であり、次のように使用できます...

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.");
        }
    }
}

あなたはUDEがGPLであることを知っている必要があり
— lindexiは

ライセンスが心配な場合は、このライセンスを使用できます。MITとしてライセンス供与されており、オープンソースソフトウェアとクローズドソースソフトウェアの両方に使用できます。nuget.org/packages/SimpleHelpers.FileEncoding
— アレクセイ・アグエロアルバ

ライセンスはGPLオプション付きのMPLです。The library is subject to the Mozilla Public License Version 1.1 (the "License"). Alternatively, it may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL").
— jbtule

このフォークは現在最もアクティブで、nugetパッケージUDE.Netstandardを持っているようです。 github.com/yinyue200/ude
— jbtule

非常に便利なライブラリで、さまざまな珍しいエンコーディングに対応しています!戦車!
— mshakurov

6

@CodesInChaosによって提案されたステップの実装の詳細を提供します。

1)バイトオーダーマークがあるかどうかを確認します。

2)ファイルが有効なUTF8かどうかを確認します

3)ローカルの「ANSI」コードページを使用します(Microsoftが定義するANSI)

ステップ2が機能するのは、UTF8以外のコードページのほとんどの非ASCIIシーケンスが有効なUTF8ではないためです。https://stackoverflow.com/a/4522251/867248は、戦術を詳細に説明しています。

using System; using System.IO; using System.Text;

// Using encoding from BOM or UTF8 if no BOM found,
// check if the file is valid, by reading all lines
// If decoding fails, use the local "ANSI" codepage

public string DetectFileEncoding(Stream fileStream)
{
    var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
    using (var reader = new StreamReader(fileStream, Utf8EncodingVerifier,
           detectEncodingFromByteOrderMarks: true, leaveOpen: true, bufferSize: 1024))
    {
        string detectedEncoding;
        try
        {
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
            }
            detectedEncoding = reader.CurrentEncoding.BodyName;
        }
        catch (Exception e)
        {
            // Failed to decode the file using the BOM/UT8. 
            // Assume it's local ANSI
            detectedEncoding = "ISO-8859-1";
        }
        // Rewind the stream
        fileStream.Seek(0, SeekOrigin.Begin);
        return detectedEncoding;
   }
}


[Test]
public void Test1()
{
    Stream fs = File.OpenRead(@".\TestData\TextFile_ansi.csv");
    var detectedEncoding = DetectFileEncoding(fs);

    using (var reader = new StreamReader(fs, Encoding.GetEncoding(detectedEncoding)))
    {
       // Consume your file
        var line = reader.ReadLine();
        ...

ありがとうございました!これで解決しました。しかし、私はreader.Peek() 代わりに 使用したいと思います while (!reader.EndOfStream) { var line = reader.ReadLine(); }
— Harison Silva

reader.Peek()ストリーム全体を読み取りません。大きなストリームでPeek()は不十分であることがわかりました。reader.ReadToEndAsync()代わりに使用しました。
— ゲイリーペンドルベリー

そしてUtf8EncodingVerifierとは何ですか?
— Peter Moore

1
@PeterMoore var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());これはutf8のエンコーディングでtry、行を読み取るときにブロックで使用されます。エンコーダーが提供されたテキストの解析に失敗した場合(テキストはutf8でエンコードされていない)、Utf8EncodingVerifierがスローされます。例外がキャッチされ、テキストがutf8ではなく、デフォルトでISO-8859-1
— Berthier Lemieux

2

以下のコードは、一部のcppまたはhまたはmlファイルがBOMなしのISO-8859-1(Latin-1)またはUTF-8でエンコードされているかどうかを判別するためのPowershellコードです(どちらもGB18030と想定されていない場合)。私はフランスで働いている中国人で、MSVCはフランスのコンピューターではLatin-1として保存し、中国のコンピューターではGBとして保存するので、システムと同僚の間でソースファイルを交換するときにエンコードの問題を回避できます。

方法は簡単です。すべての文字がx00-x7Eの間である場合、ASCII、UTF-8、Latin-1はすべて同じですが、非ASCIIファイルをUTF-8で読み取ると、特殊文字 が表示されますなので、Latin-1で読んでみてください。Latin-1では、\ x7Fと\ xAFの間は空ですが、GBはx00-xFFの間でフルを使用するため、2つの間に何かを得た場合、Latin-1ではありません。

コードはPowerShellで記述されていますが、.netを使用しているため、C#またはF#に簡単に変換できます

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in Get-ChildItem .\ -Recurse -include *.cpp,*.h, *.ml) {
    $openUTF = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::UTF8)
    $contentUTF = $openUTF.ReadToEnd()
    [regex]$regex = '�'
    $c=$regex.Matches($contentUTF).count
    $openUTF.Close()
    if ($c -ne 0) {
        $openLatin1 = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('ISO-8859-1'))
        $contentLatin1 = $openLatin1.ReadToEnd()
        $openLatin1.Close()
        [regex]$regex = '[\x7F-\xAF]'
        $c=$regex.Matches($contentLatin1).count
        if ($c -eq 0) {
            [System.IO.File]::WriteAllLines($i, $contentLatin1, $Utf8NoBomEncoding)
            $i.FullName
        } 
        else {
            $openGB = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('GB18030'))
            $contentGB = $openGB.ReadToEnd()
            $openGB.Close()
            [System.IO.File]::WriteAllLines($i, $contentGB, $Utf8NoBomEncoding)
            $i.FullName
        }
    }
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');

2

.NETはあまり役に立ちませんが、次のアルゴリズムを試すことができます。

  1. BOM(バイトオーダーマーク)でエンコードを見つけようとします...見つからない可能性が非常に高いです
  2. 異なるエンコーディングに解析してみてください

これが呼び出しです:

var encoding = FileHelper.GetEncoding(filePath);
if (encoding == null)
    throw new Exception("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");

これがコードです:

public class FileHelper
{
    /// <summary>
    /// Determines a text file's encoding by analyzing its byte order mark (BOM) and if not found try parsing into diferent encodings       
    /// Defaults to UTF8 when detection of the text file's endianness fails.
    /// </summary>
    /// <param name="filename">The text file to analyze.</param>
    /// <returns>The detected encoding or null.</returns>
    public static Encoding GetEncoding(string filename)
    {
        var encodingByBOM = GetEncodingByBOM(filename);
        if (encodingByBOM != null)
            return encodingByBOM;

        // BOM not found :(, so try to parse characters into several encodings
        var encodingByParsingUTF8 = GetEncodingByParsing(filename, Encoding.UTF8);
        if (encodingByParsingUTF8 != null)
            return encodingByParsingUTF8;

        var encodingByParsingLatin1 = GetEncodingByParsing(filename, Encoding.GetEncoding("iso-8859-1"));
        if (encodingByParsingLatin1 != null)
            return encodingByParsingLatin1;

        var encodingByParsingUTF7 = GetEncodingByParsing(filename, Encoding.UTF7);
        if (encodingByParsingUTF7 != null)
            return encodingByParsingUTF7;

        return null;   // no encoding found
    }

    /// <summary>
    /// Determines a text file's encoding by analyzing its byte order mark (BOM)  
    /// </summary>
    /// <param name="filename">The text file to analyze.</param>
    /// <returns>The detected encoding.</returns>
    private static Encoding GetEncodingByBOM(string filename)
    {
        // Read the BOM
        var byteOrderMark = new byte[4];
        using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            file.Read(byteOrderMark, 0, 4);
        }

        // Analyze the BOM
        if (byteOrderMark[0] == 0x2b && byteOrderMark[1] == 0x2f && byteOrderMark[2] == 0x76) return Encoding.UTF7;
        if (byteOrderMark[0] == 0xef && byteOrderMark[1] == 0xbb && byteOrderMark[2] == 0xbf) return Encoding.UTF8;
        if (byteOrderMark[0] == 0xff && byteOrderMark[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
        if (byteOrderMark[0] == 0xfe && byteOrderMark[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
        if (byteOrderMark[0] == 0 && byteOrderMark[1] == 0 && byteOrderMark[2] == 0xfe && byteOrderMark[3] == 0xff) return Encoding.UTF32;

        return null;    // no BOM found
    }

    private static Encoding GetEncodingByParsing(string filename, Encoding encoding)
    {            
        var encodingVerifier = Encoding.GetEncoding(encoding.BodyName, new EncoderExceptionFallback(), new DecoderExceptionFallback());

        try
        {
            using (var textReader = new StreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks: true))
            {
                while (!textReader.EndOfStream)
                {                        
                    textReader.ReadLine();   // in order to increment the stream position
                }

                // all text parsed ok
                return textReader.CurrentEncoding;
            }
        }
        catch (Exception ex) { }

        return null;    // 
    }
}

1

ここでc#を見てください

https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx

string path = @"path\to\your\file.ext";

using (StreamReader sr = new StreamReader(path, true))
{
    while (sr.Peek() >= 0)
    {
        Console.Write((char)sr.Read());
    }

    //Test for the encoding after reading, or at least
    //after the first read.
    Console.WriteLine("The encoding used was {0}.", sr.CurrentEncoding);
    Console.ReadLine();
    Console.WriteLine();
}

0

役に立つかもしれません

string path = @"address/to/the/file.extension";

using (StreamReader sr = new StreamReader(path))
{ 
    Console.WriteLine(sr.CurrentEncoding);                        
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.