Автоопределение кодировки текста Хабр

Сравнение найденных решений на автоопределение кодировки

Подготовил каталог softlandia\cpd тестовые данные с файлами в разных кодировках. Содержимое файлов очень короткое и одинаковое. Одна строка “Русский в кодировке CodePageName”. Дополнил файлами со смешением кодировок и некоторыми сложными случаями и попробовал определить.

Мне кажется, получилось забавно.

# Кодировка html/charset saintfish/chardet softlandia/cpd enca
1 CP1251 windows-1252 CP1251 CP1251 CP1251
2 CP866 windows-1252 windows-1252 CP866 CP866
3 KOI8-R windows-1252 KOI8-R KOI8-R KOI8-R
4 ISO-8859-5 windows-1252 ISO-8859-5 ISO-8859-5 ISO-8859-5
5 UTF-8 with BOM utf-8 utf-8 utf-8 utf-8
6 UTF-8 without BOM utf-8 utf-8 utf-8 utf-8
7 UTF-16LE with BOM utf-16le utf-16le utf-16le ISO-10646-UCS-2
8 UTF-16LE without BOM windows-1252 ISO-8859-1 utf-16le unknown
9 UTF-16BE with BOM utf-16le utf-16be utf-16be ISO-10646-UCS-2
10 UTF-16BE without BOM windows-1252 ISO-8859-1 utf-16be ISO-10646-UCS-2
11 UTF-32LE with BOM utf-16le utf-32le utf-32le ISO-10646-UCS-4
12 UTF-32LE without BOM windows-1252 utf-32le utf-32le ISO-10646-UCS-4
13 UTF-32BE with BOM windows-1252 utf-32be utf-32be ISO-10646-UCS-4
14 UTF-32BE without BOM windows-1252 utf-32be utf-32be ISO-10646-UCS-4
15 KOI8-R (UPPER) windows-1252 KOI8-R KOI8-R CP1251
16 CP1251 (UPPER) windows-1252 CP1251 CP1251 KOI8-R
17 CP866 & CP1251 windows-1252 CP1251 CP1251 unknown

Экранированные последовательности

Экранированные последовательности особенно часто можно увидеть в исходном коде программ. Если вы хотите узнать, что означает строка записанная таким образом, то скопируйте её в одно из полей:

  • \uXXXX — обратный слэш и u за которыми идут буквы и цифры (шестнадцатеричное число)
  • \UXXXXXXXX — обратный слэш и большая U за которыми идут буквы и цифры (шестнадцатеричное число)
  • &#DDDD; — знак амперсанд и решётка, за которыми идут четыре цифры
  • &#xXXXX; — знак амперсанд, решётка и x, за которыми следует шестнадцатеричное число
  • \xXX — обратный слэш и x, за которыми следует шестнадцатеричное число
  • \OOO — обратный слэш и большая O, за которыми идёт число в восьмеричной системе счисления.

Такие строки используются в ситуациях, когда есть опасность, что написанные буквами национального алфавита строки исказятся (например, браузер неправильно поймёт кодировку веб-страницы):

  <script>  	alert ("\xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82")  </script>

Собственный велосипед

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

Для меня такая цель не стояла. Мне достаточно определять кодировки в предположении что там есть русский язык. И второе, определять нужно по небольшому количеству символов – на 10 символах должно быть достаточно уверенное определение, а желательно вообще на 5–6 символах.

Алгоритм

Когда я обнаружил совпадение кодировок KOI8-r и CP1251 по местоположению алфавита, то на пару дней загрустил… стало понятно, что чуть-чуть придётся подумать. Получилось так.

Основные решения:

  1. Работу будем вести со слайсом байтов, для совместимости с charset.DetermineEncoding()
  2. Кодировку UTF-8 и случаи с BOM проверяем отдельно
  3. Входные данные передаём по очереди каждой кодировке. Каждая сама вычисляет два целочисленных критерия. У кого сумма двух критериев больше, тот и выиграл.

Первый критерий

Первым критерием является количество самых популярных букв русского алфавита.

Наиболее часто встречаются буквы: о, е, а, и, н, т, с, р, в, л, к, м, д, п, у. Данные буквы дают 82% покрытия. Для всех кодировок кроме KOI8-r и CP1251 я использовал только первые 9 букв: о, е, а, и, н, т, с, р, в. Этого вполне хватает для уверенного определения.

А вот для KOI8-r и CP1251 пришлось доработать напильником. Коды некоторых из этих букв совпадают, например буква о имеет в CP1251 код 0xEE при этом в KOI8-r этот код у буквы н. Для этих кодировок были взяты следующие популярные буквы.

Второй критерий

К сожалению для очень коротких случаев (общая длина русского текста 5-6 символов) встречаемость популярных букв на уровне 1-3 шт и происходит нахлёст кодировок KOI8-r и CP1251. Пришлось вводить второй критерий. Подсчёт количества пар согласная+гласная. Такие комбинации ожидаемо наиболее часто встречаются в русском языке и соответственно в той кодировке в которой число таких пар больше, та кодировка имеет больший критерий.

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

Выбор кодировки при сохранении файла

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

Если документ планируется открывать в программе, которая не поддерживает Юникод, вы можете выбрать нужную кодировку. Например, в операционной системе на английском языке можно создать документ на китайском (традиционное письмо) с использованием Юникода. Однако если такой документ будет открываться в программе, которая поддерживает китайский язык, но не поддерживает Юникод, файл можно сохранить в кодировке «Китайская традиционная (Big5)». В результате текст будет отображаться правильно при открытии документа в программе, поддерживающей китайский язык (традиционное письмо).

Примечание: Так как Юникод — это наиболее полный стандарт, при сохранении текста в других кодировках некоторые знаки могут не отображаться. Предположим, например, что документ в Юникоде содержит текст на иврите и языке с кириллицей. Если сохранить файл в кодировке «Кириллица (Windows)», текст на иврите не отобразится, а если сохранить его в кодировке «Иврит (Windows)», то не будет отображаться кириллический текст.

Если выбрать стандарт кодировки, который не поддерживает некоторые символы в файле, Word пометит их красным. Вы можете просмотреть текст в выбранной кодировке перед сохранением файла.

При сохранении файла в виде кодированного текста из него удаляется текст, для которого выбран шрифт Symbol, а также коды полей.

Выбор кодировки

  1. Откройте вкладку Файл.

  2. Выберите пункт Сохранить как.

    Чтобы сохранить файл в другой папке, найдите и откройте ее.

  3. В поле Имя файла введите имя нового файла.

  4. В поле Тип файла выберите Обычный текст.

  5. Нажмите кнопку Сохранить.

  6. Если появится диалоговое окно Microsoft Office Word — проверка совместимости, нажмите кнопку Продолжить.

  7. В диалоговом окне Преобразование файла выберите подходящую кодировку.

    • Чтобы использовать стандартную кодировку, выберите параметр Windows (по умолчанию).

    • Чтобы использовать кодировку MS-DOS, выберите параметр MS-DOS.

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

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

  8. Если появилось сообщение «Текст, выделенный красным, невозможно правильно сохранить в выбранной кодировке», можно выбрать другую кодировку или установить флажок Разрешить подстановку знаков.

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

    Если в выбранной кодировке нет эквивалентных знаков для символов, выделенных красным цветом, они будут сохранены как внеконтекстные (например, в виде вопросительных знаков).

  9. Если документ будет открываться в программе, в которой текст не переносится с одной строки на другую, вы можете включить в нем жесткие разрывы строк. Для этого установите флажок Вставлять разрывы строк и укажите нужное обозначение разрыва (возврат каретки (CR), перевод строки (LF) или оба значения) в поле Завершать строки.

К началу страницы

Особенности, с которыми я столкнулся

Чуть коснусь прелестей и проблем, связанных с golang. Раздел может быть интересен только начинающим писать на golang.

Проблемы

Лично походил по некоторым подводным камушкам из 50 оттенков Go: ловушки, подводные камни и распространённые ошибки новичков. Излишне переживая и пытаясь дуть на воду, прослышав от других о страшных ожогах от молока, переборщил с проверкой входного параметра типа io.Reader. Я проверял переменную типа io.Reader с помощью рефлексии.

//CodePageDetect - detect code page of ascii data from reader 'r' func CodePageDetect(r io.Reader, stopStr ...string) (IDCodePage, error) {     if !reflect.ValueOf(r).IsValid() {         return ASCII, fmt.Errorf("input reader is nil")     } ...

Но как оказалось в моём случае достаточно проверить на nil. Теперь всё стало проще

func CodePageDetect(r io.Reader, stopStr ...string) (IDCodePage, error) {     //test input interfase     if r == nil {         return ASCII, nil     }     //make slice of byte from input reader     buf, err := bufio.NewReader(r).Peek(ReadBufSize)     if (err != nil) && (err != io.EOF) {         return ASCII, err     } ...

вызов bufio.NewReader( r ).Peek(ReadBufSize) спокойно проходит следующий тест:

    var data *os.File     res, err := CodePageDetect(data)

В этом случае Peek() возвращает ошибку.

Разок наступил на грабли с передачей массивов по значению. Немного тупанул на попытке изменять элементы, хранящиеся в map, пробегая по ним в range…