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


295

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

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

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


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

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

Решение:

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

Ответы:


260

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

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

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

Единственный самый важный факт о кодировках

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

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


43
Я отклонил этот ответ по двум причинам. Во-первых, говорить, что «вам нужно сказать», не полезно. Кто мне скажет, и через какое средство они это сделают? Если бы я сохранил файл, кого бы я спросил? Себя? Во-вторых, статья не особенно полезна в качестве ресурса для ответа на вопрос. Эта статья представляет собой историю кодирования, написанную в стиле Дэвида Седариса. Я ценю рассказ, но он не просто / прямо отвечает на вопрос.
— Geneorama

9
@geneorama, я думаю, что статья Джоэла рассматривает ваши вопросы лучше, чем я когда-либо мог, но здесь идет ... Среда, безусловно, зависит от среды, в которой получен текст. Лучше, чтобы файл (или что-то еще) содержал эту информацию (я думаю, HTML и XML). В противном случае лицо, отправляющее текст, должно иметь возможность предоставить эту информацию. Если вы были тем, кто создал файл, как вы можете не знать, какую кодировку он использует?
— СП.

4
@geneorama, продолжение ... Наконец, я полагаю, что основная причина, по которой статья не отвечает на вопрос, заключается просто в том, что на этот вопрос нет простого ответа. Если бы вопрос был «Как я могу догадаться ...», то я бы ответил по-другому.
— СП.

1
@JV Позже я узнал, что xml / html может указывать кодировку символов, спасибо за упоминание этого полезного лакомого кусочка.
— Geneorama

1
@JV «Создать файл» может быть плохим выбором слов. Я предполагаю, что пользователь может указать кодировку файла, который он генерирует. Недавно я «создал» файл из кластера Hadoop, используя Hive, и передал его на FTP перед загрузкой на различные клиентские машины. В результате получился какой-то юникодный мусор, но я не знаю, какой шаг создал проблему. Я никогда не указывал явно кодировку. Я хотел бы, чтобы я мог проверить кодировку на каждом шаге.
— Geneorama

31

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


9
Как ни странно, моя установка Firefox 3.05 обнаруживает эту страницу как UTF-8, показывая несколько глифов с вопросительным знаком в алмазе, хотя в источнике есть метатег для Windows-1252. Изменение кодировки символов вручную показывает документ правильно.
— Devstuff

5
Ваше предложение «Если вы хотите обнаружить кодировки, отличные от UTF (т.е. без спецификации)», вводит в заблуждение; стандарт Unicode не рекомендует добавлять спецификацию в документы utf-8! (и эта рекомендация, или ее отсутствие, является источником многих головных болей). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
— Дао

Это сделано для того, чтобы вы могли объединять строки UTF-8 без накопления избыточных спецификаций. Кроме того, метка порядка байтов не требуется для UTF-8, в отличие от UTF-16, например.
— sashoalm

26

Вы пробовали порт C # для Mozilla Universal Charset Detector

Пример с 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.");
        }
    }
}    

1
Работал безупречно для типа Windows-1252.
— Бисквит

И как вы можете использовать его для чтения текстового файла в строку, используя это? CharsetDetector возвращает имя кодировки в строковом формате и все ...
— Bartosz

@Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
— PrivatePyle

15

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

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

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

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


39
«эвристика» - так что браузер не совсем его обнаруживает, он делает обоснованное предположение. «работает действительно хорошо» - значит, он не работает все время? Похоже, мы согласны.
— СП.

10
Стандарт для HTML гласит, что, если набор символов не определен в документе, его следует считать закодированным как UTF-8.
— Джон Траунтвейн

5
Что здорово, если только мы не читаем нестандартные документы HTML. Или не-HTML документы.
— Кос

2
Этот ответ неверен, поэтому мне пришлось понизить голос. Сказать, что было бы неверно, что вы не можете обнаружить кодовую страницу, неправильно. Вы можете догадаться, и ваши догадки могут быть довольно хорошими, но вы не можете «обнаружить» кодовую страницу.
— z80crew

1
@JonTrauntvein Согласно спецификациям HTML5 a character encoding declaration is required even if the encoding is US-ASCII - отсутствие декларации приводит к использованию эвристического алгоритма, а не к UTF8.
— z80crew

9

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

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

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

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

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



7

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

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


7

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

Очень хорошее решение. Можно легко обернуть тело ReadAsString () в цикл разрешенных кодировок, если разрешено более 2 кодировок (UTF-8 и ASCI 1252).
— ViRuSTriNiTy

Перепробовав кучу примеров, я наконец дошёл до твоего. Я сейчас в счастливом месте. LOL Спасибо !!!!!!!
— Седрик

Это может не быть ответом на то, как обнаружить 1252 против 1250, но это абсолютно должен быть ответ на вопрос «Как обнаружить UTF-8» с или без спецификации !!
— Чак

4

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

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

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

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

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


4

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

На Ubuntu, вы просто apt-get install uchardet.

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


На Mac с помощью homebrew:brew install uchardet
— Пол Б

3

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


Это просто «кодирующая» ссылка здесь ... и в описании сказано, что мы должны предоставить кодировку ..
— SurajS

@SurajS: Посмотрите на другие перегрузки.
— Леппи

первоначальный автор хочет определить кодировку для файла, который потенциально не имеет маркера спецификации. StreamReader обнаруживает кодировку из заголовка спецификации согласно подписи. public StreamReader (Потоковый поток, bool detectEncodingFromByteOrderMarks)
— ibondre

1

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

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

Это GPL v2.


0

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


0

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

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

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


0

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

Итак, где я впервые делал: StreamReader file = File.OpenText (fullfilename);

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

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

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


@JohnMachin Я согласен, что это редко, но это необходимо, например, в некоторых частях протокола IMAP. Если это то, где вы находитесь, вам не придется угадывать.
— tripleee

0

Откройте файл в AkelPad (или просто скопируйте / вставьте искаженный текст), перейдите в Edit -> Selection -> Recode ... -> отметьте «Autodetect».


0

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

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

MSDN


0

Спасибо @ Erik Aronesty за упоминание uchardet.

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

См. Справочную страницу chardet: https://www.commandlinux.com/man-page/man1/chardetect.1.html.

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


-1

Я использую этот код для определения кодовой страницы 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()

-1

10 лет (!) Прошло с тех пор, как об этом спросили, и все же я не вижу упоминания о хорошем решении MS, не подпадающем под GPL: IMultiLanguage2 API.

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.