Як надійно вгадати кодування між MacRoman, CP1252, Latin1, UTF-8 та ASCII


99

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

Тому було вирішено відтепер забороняти файлам мати імена, які закінчуються на *.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), а більшість настільних комп'ютерів є Macs, у нас є досить багато роздратованих файлів MacRoman. І це особливо є проблемою.

Деякий час я шукаю спосіб програмно визначити, який із них

  1. ASCII
  2. ISO-8859-1
  3. CP1252
  4. Макроман
  5. UTF-8

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

Тому я шукаю існуючу бібліотеку чи програму, яка надійно визначає, у котрому з цих п'яти кодувань знаходиться файл - і бажано більше того. Зокрема, він повинен розрізняти три цитові кодування, які я цитував, особливо MacRoman . Файли - це понад 99% тексту англійською мовою; є кілька інших мов, але не багато.

Якщо це код бібліотеки, наша мовна перевага полягає в тому, щоб він знаходився в Perl, C, Java або Python, і в такому порядку. Якщо це просто програма, то нас насправді не цікавить, на якій мові вона знаходиться, якщо вона надходить у повноцінний джерело, працює на Unix і повністю не обмежена.

Хто-небудь ще мав цю проблему із зільйоном застарілих текстових файлів, випадково закодованих? Якщо так, то як ви намагалися вирішити це та наскільки успішними були ви? Це найважливіший аспект мого питання, але я також зацікавлений у тому, чи вважаєте ви, що заохочення програмістів назвати (або перейменувати) свої файли за допомогою фактичного кодування цих файлів допоможе нам уникнути проблеми в майбутньому. Хтось коли-небудь намагався застосувати це на інституційній основі, і якщо так, то було це успішно чи ні, і чому?

І так, я повністю розумію, чому не можна гарантувати однозначну відповідь, враховуючи характер проблеми. Особливо це стосується невеликих файлів, де у вас немає достатньої кількості даних. На щастя, наші файли рідко малі. Крім випадкового READMEфайлу, більшість знаходяться в діапазоні розмірів від 50 до 250 К, а багато з них - більші. Все, що має розмір більше декількох K, гарантується англійською мовою.

Проблемною областю є видобуток біомедичного тексту, тому ми іноді маємо справу з великими та надзвичайно великими корпораціями, як і у всіх сховищах відкритого доступу PubMedCentral. Досить величезний файл - це BioThesaurus 6.0, розміром 5,7 гігабайт. Цей файл особливо дратує, оскільки він майже весь UTF-8. Однак деякі numbskull пішли і застрягли в ньому кілька рядків, які знаходяться в якомусь 8-бітному кодуванні - Microsoft CP1252, я вважаю. Мине досить багато часу, перш ніж поїхати на цьому. :(


Див stackoverflow.com/questions/4255305 / ... для розчину
mpenkov

Відповіді:


86

По-перше, прості випадки:

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, і перейдіть до того, що найбільше.


6
Я прийняв вашу відповідь, тому що жоден кращий не представився, і ви добре зробили написання тих самих питань, якими я займався. У мене дійсно є програми, щоб нюхати ці байти, хоча у вас приблизно вдвічі більше, ніж я придумав сам.
tchrist

10
Нарешті обійшов реалізацію цього. Виявляється, Вікіпедія не є хорошими даними про навчання. З 1-х випадкових статей en.wikipedia, не рахуючи розділу МОВИ, я отримав 50k кодових точок UNASCII, але розподіл не є достовірним: середня крапка та куля зависокі, & c & c & c. Тому я використав корпус відкритого доступу PubMed PubMed, майнінг + 14M кодових точок UNASCII. Я використовую їх для побудови моделі відносної частоти всіх 8-бітових кодувань, більш фантазійних, ніж ваша, але заснованих на цій ідеї. Це доводить високу прогностику кодування для біомедичних текстів цільової області. Я повинен це опублікувати. Дякую!
tchrist

5
У мене ще немає файлів MacRoman, але не було б використання CR як розмежувачів рядків, які служать корисним тестом. Це працювало б для старих версій Mac OS, хоча я не знаю про OS9.
Міллівейс

10

Додаткову документацію можна знайти тут: mozilla.org/projects/intl/detectorsrc.html , звідти випливає, що якщо ви зануритесь у документи, ви зможете знайти підтримувані схеми
Joel Berger

@Joel: Я вкопав джерело. Це було риторичне питання. x-mac-cyrillicпідтримується, x-mac-hebrewобговорюється в коментарях, x-mac-anything-elseне згадується.
Джон Махін

@John Machin: дивно, що кирилиця та іврит кивають, але нічого іншого. Я кидав лише в інше джерело документації, не читав далі, дякую за це!
Джоель Бергер

7

Моя спроба такої евристики (якщо припустити, що ви виключили ASCII та UTF-8):

  • Якщо 0x7f до 0x9f взагалі не з'являються, це, мабуть, ISO-8859-1, оскільки це дуже рідко використовуються контрольні коди.
  • Якщо від 0x91 до 0x94 з'являються багато, це, мабуть, Windows-1252, оскільки це "розумні цитати", напевно, найбільш імовірні символи в цьому діапазоні, які використовуються в англійському тексті. Щоб бути більш впевненим, ви можете шукати пари.
  • Інакше це MacRoman, особливо якщо ви бачите багато від 0xd2 до 0xd5 (саме там типографічні цитати є в MacRoman).

Бічна примітка:

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

Не роби цього!!

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


7
Стійкий компілор! Ні, ми не можемо сказати людям, що вони можуть використовувати лише ASCII; це вже не 1960-ті. Це не було б проблемою, якби була анотація @encoding, так що той факт, що джерело перебуває у певному кодуванні, не змушений був зберігатись поза вихідним кодом, справді ідіотський недолік Java, від якого не страждає ні Perl, ні Python. . Це має бути в джерелі. Це не наша основна проблема; це 1000 *.textфайлів.
tchrist

3
@tchrist: Насправді не так складно буде написати власний процесор анотацій, щоб підтримати таку примітку. І все-таки непохитний нагляд не мати його у стандартному API.
Майкл Боргвардт

Навіть якщо Java підтримувала @encoding, це не забезпечило б правильність декларації кодування .
dan04

4
@ dan04: Ви можете сказати те саме про декларацію про кодування в XML, HTML або деінде. Але так само, як і в тих прикладах, якби це було визначено у стандартному API, більшість інструментів, які працюють із вихідним кодом (особливо редактори та IDE), підтримували б його, що досить надійно не дасть людям випадково створювати файли, кодування вмісту яких не відповідає декларація.
Майкл Боргвардт

4
"Компілятор Java очікує, що назви файлів збігаються з іменами класів." Це правило застосовується лише в тому випадку, якщо файл визначає публічний клас найвищого рівня.
Метью Флашен

6

"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, але результуючий Unicode містить "символи управління" в діапазоні U + 0080 включно U + 009F ... це може бути результатом перекодування cp1252 / cp850 (бачимо це!) / Тощо файли від "ISO-8859-1" до UTF-8.

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


1
причиною впорядкування мови є навколишнє середовище. Більшість наших основних додатків, як правило, у Java, а незначні утиліти, а деякі програми - в перл. У нас тут і там трохи коду, який знаходиться в python. Я в основному програміст на C і perl, принаймні за першим вибором, тому я шукав або рішення Java для підключення до нашої бібліотеки додатків, або бібліотеку perl для того ж. Якщо C, я міг би побудувати шар клею XS, щоб підключити його до інтерфейсу perl, але я ще ніколи цього не робив у python.
tchrist

3

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

Найкраще, на що можна сподіватися, - це якийсь алгоритм, який аналізує байти, і на основі ймовірностей того, що певний байт використовується на певній мові з певним кодуванням, здогадається, яке кодування використовує файли. Але це повинно знати, якою мовою використовується цей файл, і стає абсолютно марною, коли у вас є файли зі змішаним кодуванням.

Якщо ви знаєте, що текст у файлі написаний англійською мовою, ви навряд чи помітите будь-яку різницю в залежності від того, яке кодування ви вирішите використовувати для цього файлу, оскільки відмінності між усіма згаданими кодуваннями локалізовані в частини кодувань, які задають символи, які зазвичай не використовуються в англійській мові. Можливо, у вас виникнуть проблеми, коли текст використовує спеціальне форматування або спеціальні версії пунктуації (наприклад, CP1252 має кілька версій символів цитати), але для суті тексту, ймовірно, не виникне проблем.


1

Якщо ви можете виявити кожне кодування EXCEPT для макромана, то було б логічно припустити, що ті, які неможливо розшифрувати, перебувають у макромані. Іншими словами, просто складіть список файлів, які неможливо було обробити, і обробляйте їх, як ніби вони макроманні.

Іншим способом сортування цих файлів було б зробити програму на основі сервера, яка дозволяє користувачам вирішувати, яке кодування не є прихованим. Звичайно, це буде всередині компанії, але зі 100 працівниками, які роблять по кілька кожного дня, ви матимете тисячі файлів, зроблених за один раз.

Нарешті, чи не було б краще просто перетворити всі існуючі файли в єдиний формат і вимагати, щоб нові файли були у такому форматі.


5
Смішно! Коли я вперше прочитав цей коментар після того, як його перервали протягом 30 хвилин, я прочитав "macroman" як "макросмен" і не встановив зв'язку з MacRoman, поки я не запустив пошук цього рядка, щоб побачити, чи згадувала його ОП
Адріан Пронк

+1 ця відповідь є цікавою. не впевнений, хороша це чи погана ідея. хтось може подумати про існуюче кодування, яке також може залишитися непоміченим? чи існує ймовірність у майбутньому?
ім'я користувача

1

Хто-небудь ще мав цю проблему із зільйоном застарілих текстових файлів, випадково закодованих? Якщо так, то як ви намагалися вирішити це та наскільки успішними були ви?

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

  1. Встановіть початкову ( попередню ) ймовірність того, що файл знаходиться в кодуванні, виходячи з частот кожного кодування.
  2. Вивчіть кожен байт по черзі у файлі. Знайдіть значення байта, щоб визначити кореляцію між наявним значенням байта та файлом, який фактично знаходиться в кодуванні. Використовуйте це співвідношення для обчислення нової ( задньої ) ймовірності того, що файл знаходиться в кодуванні. Якщо у вас є більше байтів для дослідження, використовуйте задню ймовірність цього байту як попередню ймовірність, коли ви вивчаєте наступний байт.
  3. Коли ви дістанетесь до кінця файлу (я фактично дивлюсь лише на перші 1024 байти), у вас є ймовірність того, що файл знаходиться в кодуванні.

З'ясовується , що Байеса теорема стає дуже легко зробити , якщо замість обчислення ймовірностей, то обчислити зміст інформації , яка є логарифмом шансів : info = log(p / (1.0 - p)).

Вам доведеться обчислити аніморіальну ймовірність initail та кореляції, вивчивши корпус файлів, які ви вручну класифікували.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.