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

295

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

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

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


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

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

Решение:

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

Ответы:

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.");
        }
    }
}    
ITmeze
источник
1
Работал безупречно для типа Windows-1252.
Бисквит
И как вы можете использовать его для чтения текстового файла в строку, используя это? CharsetDetector возвращает имя кодировки в строковом формате и все ...
Bartosz
@Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle
15

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

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

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

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

shoosh
источник
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 - в основном большая часть среды США и Западной Европы.

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

Tao
источник
7

Notepad ++ имеет эту функцию из коробки. Это также поддерживает изменение этого.

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

TarmoPikaro
источник
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, которые не начинаются с какой-либо спецификации, второй шаг будет охватывать те, которые были пропущены с первого шага.

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

tzot
источник
4

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

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

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

Эрик Аронесты
источник
На Mac с помощью homebrew:brew install uchardet
Пол Б
3

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

leppie
источник
Это просто «кодирующая» ссылка здесь ... и в описании сказано, что мы должны предоставить кодировку ..
SurajS
@SurajS: Посмотрите на другие перегрузки.
Леппи
первоначальный автор хочет определить кодировку для файла, который потенциально не имеет маркера спецификации. StreamReader обнаруживает кодировку из заголовка спецификации согласно подписи. public StreamReader (Потоковый поток, bool detectEncodingFromByteOrderMarks)
ibondre
1

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

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

Это GPL v2.

Ник Маттео
источник
0

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

DeeCee
источник
0

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

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

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

devstuff
источник
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».

plavozont
источник
0

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

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

MSDN

PrivatePyle
источник
0

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

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

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

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

Schlacki
источник
-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. Вокруг есть и другие, но в целом эта библиотека не получает того внимания, которого заслуживает.

Офек Шилон
источник