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

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

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

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.

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

9 ответов

Решение

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

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, частота ложноположительных результатов для двухбайтовой последовательности составляет всего 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 "encoding= декларация. Если присутствует, используйте эту кодировку. Если отсутствует, предположим, что UTF-8 является кодировкой XML по умолчанию.

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

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

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

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

Разумный дефолт

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

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

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

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

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

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

Вот мой код, который обнаруживает все кодировки, которые 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;
}

Использование 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, чтобы переключать логику в зависимости от нее.

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

Да, здесь есть один: http://en.wikipedia.org/wiki/Byte_order_mark.

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

Решение для всех немцев => ÄÖÜäöüß

Эта функция открывает файл и определяет кодировку по спецификации.
Если спецификация отсутствует, файл будет интерпретирован как ANSI, но если в нем есть немецкий Umlaute в кодировке UTF8, он будет обнаружен как UTF8.

      private static Encoding GetEncoding(string sFileName)
{
    using (var reader = new StreamReader(sFileName, Encoding.Default, true))
    {
        string sContent = "";
        if (reader.Peek() >= 0) // you need this!
            sContent = reader.ReadToEnd();
        Encoding MyEncoding = reader.CurrentEncoding;
        if (MyEncoding == Encoding.Default) // Ansi detected (this happens if BOM is missing)
        { // Look, if there are typical UTF8 chars in this file...
            string sUmlaute = "ÄÖÜäöüß";
            bool bUTF8CharDetected = false;
            for (int z=0; z<sUmlaute.Length; z++)
            {
                string sUTF8Letter = sUmlaute.Substring(z, 1);
                string sUTF8LetterInAnsi = Encoding.Default.GetString(Encoding.UTF8.GetBytes(sUTF8Letter));
                if (sContent.Contains(sUTF8LetterInAnsi))
                {
                    bUTF8CharDetected = true;
                    break;
                }
            }
            if (bUTF8CharDetected) MyEncoding = Encoding.UTF8;
        }
        return MyEncoding;
    }
}
Другие вопросы по тегам