Как надежно угадать кодировку между MacRoman, CP1252, Latin1, UTF-8 и ASCII


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

поэтому было решено впредь запрещать файлы с именами, которые заканчиваются на *.txt или *.text. Думая, что эти расширения вводят в заблуждение случайного программиста в тупое самодовольство относительно кодировки, и это приводит к неправильному обращению. Было бы почти лучше, если бы не было расширение вообще, потому что по крайней мере тогда вы знаю что вы не знаете, что у вас есть.

однако, мы не собираемся заходить так далеко. Вместо этого вы должны использовать имя файла, которое заканчивается в кодировке. Так что для текстовых файлов, например, это будет что-то вроде README.ascii,README.latin1,README.utf8 и т. д.

для файлов, которые требуют определенного расширения, если можно укажите кодировку внутри самого файла, например, в Perl или Python, то вы должны сделать это. Для таких файлов, как Java source, где такой объект не существует внутри файла, вы поставите кодировку перед расширением, например SomeClass-utf8.java.

для вывода UTF-8 должен быть сильно предпочтительный.

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

они по-разному в ASCII, ISO-8859-1, UTF-8, Microsoft CP1252 или Apple MacRoman. Хотя мы знаем, что мы можем сказать, если что-то ASCII, и мы стоим хорошее изменение зная, если что-то, вероятно, UTF-8, мы в тупике о 8-битных кодировках. Поскольку мы работаем в смешанной среде Unix (Solaris, Linux, Darwin) с большинством настольных компьютеров Mac, у нас есть довольно много раздражающих файлов MacRoman. И это особенно проблема.

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

  1. ASCII
  2. ISO-8859-1
  3. CP1252
  4. macroman-принятой
  5. UTF-8

файл находится в, и я не нашел программу или библиотеку, которая может надежно различать эти три разных 8-битных кодировки. У нас, наверное, есть более тысячи файлов MacRoman в одиночку, поэтому любой детектор кодировки, который мы используем, должен быть в состоянии вынюхать их. Ничто из того, что я видел, не может справиться с трюком. Я возлагал большие надежды на библиотека детекторов кодировки ICU, но он не может обрабатывать MacRoman. Я также посмотрел на модули, чтобы сделать то же самое в Perl и Python, но снова и снова это всегда одна и та же история: нет поддержки для обнаружения MacRoman.

поэтому я ищу существующую библиотеку или программа, которая надежно определяет, в какой из этих пяти кодировок находится файл-и предпочтительно больше. В частности, он должен различать три 3-битные кодировки, которые я привел,особенно macroman-принятой. Файлы более 99% текста на английском языке; есть несколько других языков, но не многие.

если это код библиотеки, наш язык предпочтительнее, чтобы он был в Perl, C, Java или Python и в этом порядке. Если это просто программа, то мы не действительно важно, на каком языке он находится, если он поставляется в полном исходном коде, работает на Unix и полностью свободен.

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

и да, я полностью понимаю, почему нельзя гарантировать определенный ответ, учитывая природу проблемы. Это особенно относится к небольшим файлам, где у вас недостаточно данных для продолжения. К счастью, наши файлы редко бывают маленькими. Кроме случайных README файл, большинство из них находятся в диапазоне размеров от 50k до 250k, и многие более крупный. Все, что больше, чем несколько K в размере гарантированно будет на английском языке.

проблемная область-это биомедицинский текстовый майнинг, поэтому мы иногда имеем дело с обширными и чрезвычайно большими корпусами, такими как все открытые доступы pubmedcentral. Довольно большой файл-это BioThesaurus 6.0, на 5.7 гигабайт. Этот файл особенно раздражает, потому что это почти все UTF-8. Тем не менее, некоторые тупицы пошли и вставили в него несколько строк, которые находятся в некоторых 8-битных кодировка-Microsoft CP1252, я полагаю. Это займет довольно много времени, прежде чем вы споткнетесь на этом. : (

7 96

7 ответов:

во-первых, простым случаях:

ASCII

Если ваши данные не содержат байтов выше 0x7F, то это ASCII. (Или 7-битная кодировка ISO646, но они очень устарели.)

UTF-8

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

ISO-8859-1 против windows-1252

единственная разница между этими две кодировки-это то, что ISO-8859-1 имеет контрольные символы C1, где windows-1252 имеет печатные символы €'ƒ"...†‰ ‰ š "žž""•--™š"œžÿ. Я видел много файлов, которые используют фигурные кавычки или тире, но ни один из них не использует управляющие символы C1. Так что даже не беспокойтесь о них, или ISO-8859-1, просто обнаружьте windows-1252 вместо этого.

теперь у вас остается только один вопрос.

как вы отличаете MacRoman от cp1252?

Это много похитрее.

неопределено символов

байты 0x81, 0x8D, 0x8F, 0x90, 0x9D не используются в windows-1252. Если они происходят, то предположим, что данные являются MacRoman.

одинаковых символов

байты 0xA2 ( ¢ ), 0xA3 ( £ ), 0xA9 ( © ), 0xB1 ( ± ), 0xB5 (µ) оказываются одинаковыми в обеих кодировках. Если это единственные байты, отличные от ASCII, то не имеет значения, выбираете ли вы MacRoman или cp1252.

статистические подходите

счетчик символов (не байт!) частоты в данных, которые вы знаете, чтобы быть UTF-8. Определите наиболее частые символы. Затем используйте эти данные, чтобы определить, являются ли символы cp1252 или MacRoman более распространенными.

например, в поиске, который я только что выполнил на 100 случайных английских статьях Википедии, наиболее распространенными символами без ASCII являются ·•–é°®’èö—. Исходя из этого факта,

  • байты 0x92, 0x95, 0x96, 0x97, 0xAE, 0xB0, 0xB7, 0xE8, 0xE9, или 0xF6 предлагает windows-1252.
  • байты 0x8E, 0x8F, 0x9A, 0xA1, 0xA5, 0xA8, 0xD0, 0xD1, 0xD5 или 0xE1 предлагают MacRoman.

подсчитайте cp1252-предлагая байты и MacRoman-предлагая байты, и идите с тем, что больше всего.

Mozilla nsUniversalDetector (привязка на Perl: Кодировка:Обнаружьте/Encode::Detect:: Detector) доказано в миллион раз.

моя попытка такой эвристики (предполагая, что вы исключили ASCII и UTF-8):

  • если 0x7f до 0x9f не появляются вообще, это, вероятно, ISO-8859-1, потому что они очень редко используются управляющие коды.
  • если 0x91 через 0x94 появляются в lot, это, вероятно, Windows-1252, потому что это "умные кавычки", безусловно, наиболее вероятные символы в этом диапазоне, которые будут использоваться в английском тексте. Чтобы быть более уверенным, вы можете искать пары.
  • в противном случае, это MacRoman, особенно если вы видите много 0xd2 через 0xd5 (вот где типографские кавычки находятся в MacRoman).

Примечание:

для таких файлов, как Java source, где нет такое средство существует внутренне к файл, вы поставите кодировку перед расширение, например SomeClass-utf8.java

не делай этого!!

компилятор Java ожидает, что имена файлов будут совпадать с именами классов, поэтому переименование файлы сделают исходный код не компилируемым. Правильнее было бы угадать кодировку, а затем использовать native2ascii инструмент для преобразования всех символов, отличных от ASCII, в escape-последовательности Unicode.

"Perl, C, Java или Python, и в таком порядке": интересное отношение : -)

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

стратегии UTF-8 (на наименее предпочтительном языке):

# 100% Unicode-standard-compliant UTF-8
def utf8_strict(text):
    try:
        text.decode('utf8')
        return True
    except UnicodeDecodeError:
        return False

# looking for almost all UTF-8 with some junk
def utf8_replace(text):
    utext = text.decode('utf8', 'replace')
    dodgy_count = utext.count(u'\uFFFD') 
    return dodgy_count, utext
    # further action depends on how large dodgy_count / float(len(utext)) is

# checking for UTF-8 structure but non-compliant
# e.g. encoded surrogates, not minimal length, more than 4 bytes:
# Can be done with a regex, if you need it

Как только вы решили, что это ни ASCII, ни UTF-8:

Как отмечали другие, у вас действительно есть только символы пунктуации с высоким разрядом, доступные для различают cp1252 и macroman. Я бы предложил обучать модель типа Mozilla на ваших собственных документах, а не на Шекспире или Хансарде или Библии KJV, и учитывать все 256 байт. Я предполагаю, что ваши файлы не имеют разметки (HTML, XML и т. д.) В них-это исказит вероятности чего-то шокирующего.

вы упомянули файлы, которые в основном UTF-8, но не могут декодировать. Вы также должны быть очень подозрительны:

(1) файлы, которые, якобы, закодированы в ISO-8859-1, но содержат "управляющие символы" в диапазоне от 0x80 до 0x9F включительно ... это настолько распространено, что проект стандарта HTML5 говорит декодировать все потоки HTML, объявленные как ISO-8859-1 с помощью cp1252.

(2) файлы, которые декодируют OK как UTF-8, но результирующий Unicode содержит "управляющие символы" в диапазоне от U+0080 до U+009F включительно ... это может произойти в результате перекодирования cp1252 / cp850 (видел, как это происходит!) / etc файлы от "ISO-8859-1" до UTF-8.

Background: у меня есть проект wet-Sunday-afternoon для создания детектора кодировок на основе Python, ориентированного на файлы (вместо веб-ориентированного) и хорошо работающего с 8-битными наборами символов, включая legacy ** n, как cp437 и cp850. Это еще далеко не прайм-тайм. Меня интересуют учебные файлы; ваш ISO-8859-1 / файлы cp1252 / MacRoman также "необременены", как вы ожидаете, что чье-либо кодовое решение будет?

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

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

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

Если вы можете обнаружить все кодировки, кроме macroman, чем было бы логично предположить, что те, которые не могут быть расшифрованы в macroman. Другими словами, просто составьте список файлов, которые не могут быть обработаны и обрабатывать их, как если бы они были macroman.

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

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

у кого-нибудь еще была эта проблема с миллионами устаревших текстовых файлов, случайно закодированных? Если да, то как вы пытались решить эту проблему и насколько успешно?

в настоящее время я пишу программу, которая переводит файлы в XML. Он должен автоматически определять тип каждого файла, который является надмножеством задачи определения кодировки текстового файла. Для определения кодировки я использую байесовский подход. То есть, мой классификационный код вычисляет a вероятность (правдоподобие) того, что текстовый файл имеет определенную кодировку для всех кодировок, которые он понимает. Потом программа выбирает наиболее вероятный декодер. Байесовский подход работает так для каждой кодировки.

  1. установить начальную (до) вероятность того, что файл находится в кодировке, на основе частот каждой кодировки.
  2. проверьте каждый байт по очереди в файле. Найдите значение байта, чтобы определить корреляцию между этим байтом значение присутствует, и файл фактически находится в этой кодировке. Используйте эту корреляцию для вычисления нового (кзади) вероятность того, что файл находится в кодировке. Если у вас есть больше байтов для проверки, используйте заднюю вероятность этого байта в качестве предыдущей вероятности при проверке следующего байта.
  3. когда Вы дойдете до конца файла (я на самом деле смотрю только на первые 1024 байта), проверяемость у вас есть вероятность того, что файл находится в кодирование.

оказывается, что теорема Байеса становится очень легко сделать, если вместо вычисления вероятностей, вычислить информационный контент, который является логарифмом шансы:info = log(p / (1.0 - p)).

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