Как определить кодировку символов текстового файла?

Я пытаюсь определить, какая кодировка символов используется в моем файле.

Я пытаюсь с этим кодом получить стандартное кодирование

public static Encoding GetFileEncoding(string srcFile)
    {
      // *** Use Default of Encoding.Default (Ansi CodePage)
      Encoding enc = Encoding.Default;

      // *** Detect byte order mark if any - otherwise assume default
      byte[] buffer = new byte[5];
      FileStream file = new FileStream(srcFile, FileMode.Open);
      file.Read(buffer, 0, 5);
      file.Close();

      if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
        enc = Encoding.UTF8;
      else if (buffer[0] == 0xfe && buffer[1] == 0xff)
        enc = Encoding.Unicode;
      else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
        enc = Encoding.UTF32;
      else if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
        enc = Encoding.UTF7;
      else if (buffer[0] == 0xFE && buffer[1] == 0xFF)      
        // 1201 unicodeFFFE Unicode (Big-Endian)
        enc = Encoding.GetEncoding(1201);      
      else if (buffer[0] == 0xFF && buffer[1] == 0xFE)      
        // 1200 utf-16 Unicode
        enc = Encoding.GetEncoding(1200);


      return enc;
    }

Мой пять первых байтов - 60, 118, 56, 46 и 49.

Есть ли диаграмма, которая показывает, какая кодировка соответствует этим пяти первым байтам?

Ответ 1

Вы не можете зависеть от файла, имеющего спецификацию. UTF-8 не требует этого. И кодировки, отличные от Unicode, даже не имеют спецификации. Существуют, однако, другие способы обнаружения кодирования.

UTF-32

BOM - 00 00 FE FF (для BE) или FF FE 00 00 (для LE).

Но UTF-32 легко обнаружить даже без спецификации. Это связано с тем, что диапазон кодовой точки Юникода ограничен U + 10FFFF, и, таким образом, единицы UTF-32 всегда имеют шаблон 00 {0x | 10} xx xx (для BE) или xx xx {0x | 10} 00 (для LE), Если данные имеют длину, кратную 4, и следуют одному из этих шаблонов, вы можете смело предположить, что это UTF-32. Ложные срабатывания почти невозможны из-за редкости 00 байтов в байтовом кодировании.

US-ASCII

Нет спецификации, но вам она не нужна. ASCII можно легко идентифицировать по отсутствию байтов в диапазоне 80 FF.

UTF-8

BOM - EF BB BF. Но вы не можете полагаться на это. Многие файлы UTF-8 не имеют спецификации, особенно если они возникли в системах, отличных от Windows.

Но вы можете смело предположить, что если файл проверяется как UTF-8, это UTF-8. Ложные срабатывания встречаются редко.

В частности, учитывая, что данные не являются ASCII, ложная положительная скорость для 2-байтовой последовательности составляет всего 3,9% (1920/49152). Для 7-байтовой последовательности это менее 1%. Для 12-байтовой последовательности она составляет менее 0,1%. Для 24-байтовой последовательности она меньше 1 в миллионе.

UTF-16

BOM - FE FF (для BE) или FF FE (для LE). Обратите внимание, что спецификация UTF-16LE находится в начале спецификации UTF-32LE, поэтому сначала проверьте UTF-32.

Могут быть файлы UTF-16 без спецификации, но было бы очень сложно их обнаружить. Единственный надежный способ распознавания UTF-16 без спецификации - искать суррогатные пары (D [8-B] xx D [CF] xx), но символы, отличные от BMP, слишком редко используются, чтобы сделать этот подход практичным.

XML

Если ваш файл начинается с байтов 3C 3F 78 6D 6C (то есть символов ASCII "<? xml" ), тогда найдите объявление encoding=. Если присутствует, используйте эту кодировку. Если отсутствует, то предположим, что UTF-8, который является стандартным XML-кодированием.

Если вам необходимо поддерживать EBCDIC, также ищите эквивалентную последовательность 4C 6F A7 94 93.

В общем случае, если у вас есть формат файла, который содержит объявление кодировки, тогда найдите это объявление, а не пытайтесь угадать кодировку.

Ничего из вышеперечисленного

Существуют сотни других кодировок, которые требуют больше усилий для обнаружения. Я рекомендую попробовать детектор charset Mozilla или порт .NET.

Ответ 2

Если вы хотите продолжить "простое" решение, вы можете найти этот класс, который я сочтет полезным:

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

Сначала он автоматически определяет обнаружение спецификации, а затем пытается различать кодировки Unicode без спецификации, а также другую кодировку по умолчанию (обычно Windows-1252, некорректно обозначенную как Encoding.ASCII в .Net).

Как отмечалось выше, более "более тяжелое" решение с участием NCharDet или MLang может быть более уместным, и, как я отмечаю на обзорной странице этого класса, лучше всего обеспечить некоторую интерактивность с пользователем, если это вообще возможно, потому что нет возможности 100% -ной скорости обнаружения!

Ответ 3

Используйте StreamReader и направьте его для обнаружения кодировки для вас:

using (var reader = new System.IO.StreamReader(path, true))
{
    var currentEncoding = reader.CurrentEncoding;
}

И используйте Идентификаторы кодовой страницы https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx для переключения логики в зависимости от нее.

Ответ 4

Несколько ответов здесь, но никто не опубликовал полезный код.

Вот мой код, который обнаруживает все кодировки, обнаруженные Microsoft в Framework 4 в классе StreamReader.

Очевидно, вы должны вызвать эту функцию сразу после открытия потока, прежде чем читать что-либо из потока, потому что спецификация - это первые байты в потоке.

Эта функция требует потока, который может искать (например, FileStream). Если у вас есть Stream, который не может найти вас, вы должны написать более сложный код, который возвращает буфер байта с байтами, которые уже были прочитаны, но которые не являются спецификацией.

/// <summary>
/// UTF8    : EF BB BF
/// UTF16 BE: FE FF
/// UTF16 LE: FF FE
/// UTF32 BE: 00 00 FE FF
/// UTF32 LE: FF FE 00 00
/// </summary>
public static Encoding DetectEncoding(Stream i_Stream)
{
    if (!i_Stream.CanSeek || !i_Stream.CanRead)
        throw new Exception("DetectEncoding() requires a seekable and readable Stream");

    // Try to read 4 bytes. If the stream is shorter, less bytes will be read.
    Byte[] u8_Buf = new Byte[4];
    int s32_Count = i_Stream.Read(u8_Buf, 0, 4);
    if (s32_Count >= 2)
    {
        if (u8_Buf[0] == 0xFE && u8_Buf[1] == 0xFF)
        {
            i_Stream.Position = 2;
            return new UnicodeEncoding(true, true);
        }

        if (u8_Buf[0] == 0xFF && u8_Buf[1] == 0xFE)
        {
            if (s32_Count >= 4 && u8_Buf[2] == 0 && u8_Buf[3] == 0)
            {
                i_Stream.Position = 4;
                return new UTF32Encoding(false, true);
            }
            else
            {
                i_Stream.Position = 2;
                return new UnicodeEncoding(false, true);
            }
        }

        if (s32_Count >= 3 && u8_Buf[0] == 0xEF && u8_Buf[1] == 0xBB && u8_Buf[2] == 0xBF)
        {
            i_Stream.Position = 3;
            return Encoding.UTF8;
        }

        if (s32_Count >= 4 && u8_Buf[0] == 0 && u8_Buf[1] == 0 && u8_Buf[2] == 0xFE && u8_Buf[3] == 0xFF)
        {
            i_Stream.Position = 4;
            return new UTF32Encoding(true, true);
        }
    }

    i_Stream.Position = 0;
    return Encoding.Default;
}

Ответ 7

Если ваш файл начинается с байтов 60, 118, 56, 46 и 49, тогда у вас есть двусмысленный случай. Это может быть UTF-8 (без спецификации) или любой из однобайтовых кодировок, таких как ASCII, ANSI, ISO-8859-1 и т.д.

Ответ 8

Я использую Ude, который является портом С# универсального детектора Charset Mozilla. Он прост в использовании и дает некоторые действительно хорошие результаты.