Как надежно угадать кодировку между MacRoman, CP1252, Latin1, UTF-8 и ASCII - PullRequest
99 голосов
/ 16 ноября 2010

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

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

Однако мы не собираемся идти так далеко. Вместо этого вы должны будете использовать имя файла, которое заканчивается в кодировке. Например, для текстовых файлов это будет что-то вроде 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. И это особенно проблема.

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

  1. ASCII
  2. ISO-8859-1
  3. 1033 * кодировка CP1252 *
  4. MacRoman
  5. UTF-8

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

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

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

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

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

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

Ответы [ 7 ]

85 голосов
/ 17 ноября 2010

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

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, и выберите любойнаибольшие.

10 голосов
/ 17 ноября 2010
7 голосов
/ 17 ноября 2010

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

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

Примечание:

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

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

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

6 голосов
/ 17 ноября 2010

«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 говорится о декодировании ALL Потоки HTML, объявленные как ISO-8859-1 с использованием cp1252.

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

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

3 голосов
/ 17 ноября 2010

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

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

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

1 голос
/ 10 февраля 2012

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

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

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

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

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

1 голос
/ 17 ноября 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...