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

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

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

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

Для файлов, требующих определенного расширения, если вы можете указать кодировку внутри самого файла, например, в Perl или Python, тогда вы сделаете это. Для таких файлов, как источник Java, где такой объект не существует внутри файла, вы помещаете кодировку перед расширением, например 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. И это особенно проблема.

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

  • ASCII
  • ISO-8859-1
  • кодировка CP1252
  • MacRoman
  • UTF-8

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

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

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

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

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

Проблемная область - это биомедицинский интеллектуальный анализ текста, поэтому мы иногда имеем дело с обширными и чрезвычайно крупными корпорациями, такими как весь репозиторий PubMedCentrals Open Access. Довольно огромный файл - BioThesaurus 6.0, 5,7 гигабайта. Этот файл особенно раздражает, потому что это почти все UTF-8. Тем не менее, некоторые numbskull пошли и застряли несколько строк в нем, которые, по-моему, в некотором 8-битном кодировании - Microsoft CP1252. Это займет довольно много времени, прежде чем вы отправитесь в путешествие.: (

Ответ 1

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

ASCII

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

UTF-8

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

ISO-8859-1 по сравнению с окнами-1252

Единственное различие между этими двумя кодировками состоит в том, что ISO-8859-1 имеет управляющие символы C1, в которых окна-1252 имеют печатаемые символы €, ƒ "... † ‡ ‰ Š <ŒŽ '" "• --~ ™ š > œžŸ. Я видел много файлов, в которых используются фигурные кавычки или тире, но ни один из них не использует символы управления C1. Так что даже не мешайте им, или ISO-8859-1, вместо этого просто обнаруживайте Windows-1252.

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

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

Это намного сложнее.

Undefined символы

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

Идентичные символы

В обоих кодировках байты 0xA2 (¢), 0xA3 (E), 0xA9 (©), 0xB1 (±), 0xB5 (μ) совпадают. Если это единственные байты, отличные от ASCII, то неважно, выберете ли вы MacRoman или cp1252.

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

Считайте символы (NOT byte!) в данных, которые, как известно, 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, и переходите в зависимости от того, что больше всего.

Ответ 3

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

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

Боковое примечание:

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

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

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

Ответ 4

"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:

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

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

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

Ответ 7

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

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

  • Задайте начальную (предыдущую) вероятность того, что файл находится в кодировке, на основе частот каждой кодировки.
  • Осмотрите каждый байт по очереди в файле. Посмотрите на значение байта, чтобы определить соотношение между указанным значением байта и фактическим файлом в этой кодировке. Используйте эту корреляцию, чтобы вычислить новую (заднюю) вероятность того, что файл находится в кодировке. Если у вас больше байтов для проверки, используйте заднюю вероятность этого байта как предыдущую вероятность, когда вы исследуете следующий байт.
  • Когда вы дойдете до конца файла (я на самом деле смотрю только первые 1024 байта), у вас есть вероятность того, что файл находится в кодировке.

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

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