Как определить кодировку символов текстового файла? - PullRequest
72 голосов
/ 23 декабря 2010

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

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

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.

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

Ответы [ 8 ]

79 голосов
/ 23 декабря 2010

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

UTF-32

Спецификация: 00 00 FE FF (для BE) или FF FE 00 00 (для LE).

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

US-ASCII

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

UTF-8

Спецификация 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

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

Если у вас есть файл, который состоит в основном из символов ISO-8859-1, то наличие половины байтов файла, равного 00, также будет сильным показателем UTF-16.

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

XML

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

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

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

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

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

Разумное значение по умолчанию

Если вы исключили кодировки UTF и не имеете декларации кодировки или статистического обнаружения, указывающего на другую кодировку, предположим, ISO-8859-1 или тесно связанный Окна-1252 . (Обратите внимание, что новейший стандарт HTML требует декларации «ISO-8859-1» для интерпретации как Windows-1252.) Является кодовой страницей Windows по умолчанию для английского языка (и других популярных языков, таких как испанский, португальский, Немецкий и французский), это наиболее часто встречающаяся кодировка, отличная от UTF-8.

9 голосов
/ 29 апреля 2011

Если вы хотите найти «простое» решение, вам может пригодиться этот класс, который я собрал:

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

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

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

5 голосов
/ 23 декабря 2010

Используйте 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 голосов
/ 09 ноября 2016

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

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

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

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

/// <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;
}
2 голосов
/ 23 декабря 2010
2 голосов
/ 23 декабря 2010
1 голос
/ 01 августа 2014

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

1 голос
/ 23 декабря 2010

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

...