Как я могу определить кодировку / кодовую страницу текстового файла - PullRequest
286 голосов
/ 18 сентября 2008

В нашем приложении мы получаем текстовые файлы (.txt, .csv и т. Д.) Из разных источников. При чтении эти файлы иногда содержат мусор, поскольку файлы были созданы в другой / неизвестной кодовой странице.

Есть ли способ (автоматически) обнаружить кодовую страницу текстового файла?

detectEncodingFromByteOrderMarks в конструкторе StreamReader работает для UTF8 и других файлов, помеченных юникодом, но я ищу способ обнаружения кодовых страниц, таких как ibm850, windows1252.


Спасибо за ваши ответы, вот что я сделал.

Файлы, которые мы получаем от конечных пользователей, не имеют понятия о кодовых страницах. Получатели также являются конечными пользователями, и теперь они знают о кодовых страницах: кодовые страницы существуют и раздражают.

Решение:

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

Ответы [ 20 ]

256 голосов
/ 18 сентября 2008

Вы не можете обнаружить кодовую страницу, вам нужно сказать это. Вы можете проанализировать байты и угадать это, но это может дать некоторые странные (иногда забавные) результаты. Я не могу найти его сейчас, но я уверен, что Блокнот можно обмануть для отображения английского текста на китайском языке.

В любом случае, это то, что вам нужно прочитать: Абсолютный минимум Каждый разработчик программного обеспечения Абсолютно, положительно должен знать о Unicode и наборах символов (без оправданий!) .

В частности, Джоэл говорит:

Самый важный факт о кодировках

Если вы полностью забудете все, что я только что объяснил, пожалуйста, запомните один чрезвычайно важный факт. Не имеет смысла иметь строку, не зная, какую кодировку она использует. Вы больше не можете засовывать голову в песок и делать вид, что «простой» текст - это ASCII. Там нет такого понятия, как простой текст.

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

31 голосов
/ 18 сентября 2008

Если вы хотите обнаружить кодировки, отличные от UTF (т. Е. Нет спецификации), вы в основном приступаете к эвристике и статистическому анализу текста. Возможно, вы захотите взглянуть на документ Mozilla об универсальном обнаружении кодировки ( та же ссылка, с лучшим форматированием через Wayback Machine ).

25 голосов
/ 23 июля 2012

Вы пробовали порт C # для универсального детектора Charz * Mozilla

Пример из http://code.google.com/p/ude/

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.");
        }
    }
}    
15 голосов
/ 18 сентября 2008

Вы не можете обнаружить кодовую страницу

Это явно неверно. Каждый веб-браузер имеет своего рода универсальный детектор кодировки для работы со страницами, которые не имеют никаких признаков кодировки. У Firefox есть один. Вы можете скачать код и посмотреть, как он это делает. См. Некоторую документацию здесь . По сути, это эвристика, но она действительно хорошо работает.

При наличии достаточного количества текста можно даже определить язык.

Вот еще один Я только что нашел с помощью Google:

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

Я знаю, что уже слишком поздно для этого вопроса, и это решение не понравится некоторым (из-за его англо-ориентированной предвзятости и отсутствия статистического / эмпирического тестирования), но оно очень хорошо для меня, особенно для обработки загруженных Данные CSV:

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

Преимущества:

  • Спецификация обнаружения встроенного
  • Настраиваемая / резервная кодировка по умолчанию
  • довольно надежно (по моему опыту) для западноевропейских файлов, содержащих некоторые экзотические данные (например, французские имена) со смесью файлов в стиле UTF-8 и Latin-1 - в основном это большая часть среды США и Западной Европы .

Примечание: я тот, кто написал этот класс, так что, очевидно, возьмите его с крошкой соли! :)

7 голосов
/ 06 августа 2010

Блокнот ++ имеет эту функцию "из коробки". Он также поддерживает его изменение.

7 голосов
/ 19 октября 2013

В поисках другого решения я обнаружил, что

https://code.google.com/p/ude/

это решение довольно тяжелое.

Мне нужно было базовое обнаружение кодировки, основанное на 4 первых байтах и, возможно, обнаружение кодировки xml - поэтому я взял пример исходного кода из Интернета и добавил слегка измененную версию

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

написано для Java.

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

Достаточно прочитать первые 1024 байта из файла, но я загружаю весь файл.

5 голосов
/ 09 февраля 2016

Если кто-то ищет решение на 93,9%. Это работает для меня:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}
4 голосов
/ 18 сентября 2008

Я сделал нечто подобное в Python. По сути, вам нужно много примеров данных из различных кодировок, которые разбиты скользящим двухбайтовым окном и хранятся в словаре (хэш), который основан на байтовых парах, предоставляющих значения списков кодировок.

Учитывая, что словарь (хэш), вы берете введенный текст и:

  • если он начинается с любого символа спецификации ('\ xfe \ xff' для UTF-16-BE, '\ xff \ xfe' для UTF-16-LE, '\ xef \ xbb \ xbf' для UTF-8 и т. Д. ), Я отношусь к этому как предложено
  • если нет, то возьмите достаточно большой образец текста, возьмите все пары байтов образца и выберите кодировку, наименее распространенную из словаря.

Если вы также сэмплировали тексты в кодировке UTF, которые не начинаются с любой спецификации, второй шаг будет охватывать те, которые были пропущены с первого шага.

Пока что это работает для меня (примерные данные и последующие входные данные являются субтитрами на разных языках) с уменьшением количества ошибок.

3 голосов
/ 18 сентября 2008

Конструктор класса StreamReader принимает параметр 'обнаружения кодирования'.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...