Эффективный способ найти кодировку любого файла


да является наиболее частым вопросом, и этот вопрос является неопределенным для меня, и так как я не знаю много об этом.

но я бы очень точный способ найти кодировку файлов. Так точно, как Notepad++.

7 76

7 ответов:

The StreamReader.CurrentEncoding свойство редко возвращает правильную кодировку текстового файла для меня. Я добился большего успеха в определении endianness файла, проанализировав его метку порядка байтов (BOM):

/// <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) 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 Encoding.UTF32;
    return Encoding.ASCII;
}

в качестве примечания вы можете изменить последнюю строку этого метода, чтобы вернуть Encoding.Default вместо этого по умолчанию возвращается кодировка для текущей кодовой страницы ANSI ОС.

следующий код отлично работает для меня, используя StreamReader класс:

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

фокус в том, чтобы использовать Peek вызов, в противном случае .NET ничего не сделал (и он не прочитал преамбулу, спецификацию). Конечно, если вы используете любой другой ReadXXX вызов перед проверкой кодировки, он тоже работает.

если в файле нет спецификации, то defaultEncodingIfNoBom кодировка будет использоваться. Существует также StreamReader без этого метода перегрузки (в этом случае кодировка по умолчанию (ANSI будет использоваться как defaultEncodingIfNoBom), но я рекомендую определить, что вы считаете кодировкой по умолчанию в вашем контексте.

я успешно протестировал это с файлами с BOM для UTF8, UTF16 / Unicode (LE & BE) и UTF32 (LE & BE). Это не работает для UTF7.

Я бы попробовал следующие действия:

1) Проверьте, есть ли Знак порядка байтов

2) Проверьте, если файл является допустимым UTF8

3) Используйте локальную кодовую страницу " ANSI "(ANSI, как его определяет Microsoft)

Шаг 2 работает, потому что большинство не ASCII-последовательностей в кодовых страницах, кроме UTF8, не являются допустимыми UTF8.

проверить это.

Удэ

это порт 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.");
        }
    }
}

ищите здесь 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();
}

следующие коды - это мои коды Powershell для определения того, кодируются ли некоторые файлы cpp или h или ml с помощью ISO-8859-1(Latin-1) или UTF-8 без спецификации, если ни один из них не предполагает, что это GB18030. Я китаец, работающий во Франции, и MSVC сохраняет как Latin-1 на французском компьютере и сохраняет как GB на китайском компьютере, поэтому это помогает мне избежать проблемы кодирования при обмене исходными файлами между моей системой и моими коллегами.

путь прост, если все символы находятся между x00-x7E, ASCII, UTF-8 и Latin-1 все одинаковы, но если я прочитаю файл без ASCII по UTF-8, мы найдем специальный символ�, поэтому попробуйте прочитать с помощью Latin-1. В Latin-1 между \x7F и \xAF пусто, в то время как GB использует полный между x00-xFF, поэтому, если у меня есть между ними, это не 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');

предоставление сведений о реализации шагов, предложенных @CodesInChaos:

1) Проверьте, есть ли Знак порядка байтов

2) Проверьте, если файл является допустимым UTF8

3) Используйте локальную кодовую страницу " ANSI "(ANSI, как его определяет Microsoft)

Шаг 2 работает, потому что большинство не ASCII-последовательностей в кодовых страницах, кроме UTF8, не являются допустимыми 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();
        ...