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

StackOverflow https://stackoverflow.com/questions/90838

Вопрос

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

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

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


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

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

Решение:

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

Решение

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

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

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

Самый Важный Факт О Кодировках

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

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

Другие советы

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

Вы пробовали Порт C # для универсального детектора кодировок 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.");
        }
    }
}    

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

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

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

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

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

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

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

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

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

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

Ища другое решение, я обнаружил, что

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 байта из файла, но я загружаю файл целиком.

Если кто-то ищет 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();
        }
    }
}

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

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

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

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

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

Конструктор класса StreamReader принимает параметр "определить кодировку".

Инструмент "uchardet" хорошо справляется с этой задачей, используя модели распределения частот символов для каждой кодировки.Файлы большего размера и более "типичные" файлы вызывают больше доверия (очевидно).

В ubuntu вы просто apt-get install uchardet.

В других системах получите исходный код, использование и документы здесь: https://github.com/BYVoid/uchardet

Если у вас есть ссылка на библиотеку C, вы можете использовать libenca.Видишь http://cihar.com/software/enca/.Со справочной страницы:

Enca считывает заданные текстовые файлы или стандартный ввод, когда их нет, и использует знания об их языке (должны поддерживаться вами) и смесь синтаксического анализа, статистического анализа, угадывания и черной магии чтобы определить их кодировки.

Это GPL v2.

Столкнулся с той же проблемой, но пока не нашел хорошего решения для ее автоматического обнаружения.Теперь я использую для этого PSPad (www.pspad.com) ;) Работает нормально

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

Большинство людей (или приложений) каждый раз выполняют работу практически в одном и том же порядке, часто на одном и том же компьютере, поэтому вполне вероятно, что когда Боб создает csv-файл и отправляет его Мэри, он всегда будет использовать Windows-1252 или что-то еще, что установлено на его компьютере по умолчанию.

Там, где это возможно, небольшое обучение клиентов тоже никогда не помешает :-)

На самом деле я искал общий, не программный способ определения кодировки файла, но я его пока не нашел.Что я обнаружил, протестировав с разными кодировками, так это то, что мой текст был UTF-7.

Итак, где я сначала занимался:Файл StreamReader = Файл.Открытый текст (полное имя файла);

Мне пришлось изменить его на:Файл StreamReader = новый StreamReader(полное имя файла, System.Text.Encoding.UTF7);

OpenText предполагает, что это UTF-8.

вы также можете создать StreamReader следующим образом новый StreamReader(fullfilename, true), второй параметр, означающий, что он должен попытаться определить кодировку по byteordermark файла, но в моем случае это не сработало.

Откройте файл в AkelPad (или просто скопируйте / вставьте искаженный текст), перейдите в меню Правка -> Выделение -> Перекодировать...-> установите флажок "Автоматическое определение".

В качестве дополнения к сообщению ITmeze я использовал эту функцию для преобразования выходных данных порта C # для Mozilla Universal Charset Detector

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN

Спасибо @Эрик Аронести для упоминания uchardet.

Между тем (тот же самый?) инструмент существует и для Linux: chardet.
Или, возможно, в cygwin вы захотите использовать: chardetect.

Видишь: справочная страница chardet: https://www.commandlinux.com/man-page/man1/chardetect.1.html

Это позволит эвристически определить (угадать) кодировку символов для каждого данного файла и сообщит имя и уровень достоверности для обнаруженной кодировки символов каждого файла.

Прошло 10 ЛЕТ (!) с тех пор, как был задан этот вопрос, и до сих пор я не вижу никаких упоминаний о хорошем решении MS, не использующем GPL: Многоязычный2 API.

Большинство уже упомянутых библиотек основаны на UDE от Mozilla - и кажется разумным, что браузеры уже решали подобные проблемы.Я не знаю, каково решение Chrome, но поскольку IE 5.0 MS выпустили свое, и это:

  1. Свободен от проблем с лицензированием GPL и подобных им,
  2. Поддерживаемый и поддерживаемый, вероятно, вечно,
  3. Предоставляет расширенные выходные данные - все допустимые кандидаты для кодирования / кодовых страниц вместе с показателями достоверности,
  4. Удивительно прост в использовании (это один вызов функции).

Это собственный COM-вызов, но вот несколько очень хороших работ автор: Карстен Цоймер, который обрабатывает беспорядок взаимодействия для использования .net.Есть и другие библиотеки, но по большому счету эта библиотека не привлекает того внимания, которого заслуживает.

Я использую этот код для определения кодовой страницы ansi в Юникоде и Windows по умолчанию при чтении файла.Для других кодировок необходима проверка содержимого вручную или с помощью программирования.Это можно использовать для сохранения текста в той же кодировке, что и при его открытии.(Я использую VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top