Пример: перенос chardet на Python 3

Содержание
  • 1 Погружение
  • 2 Что такое автоматическое определение кодировки символов?
    • 2.1 Разве это возможно?
    • 2.2 Такой алгоритм существует?
  • 3 Введение в модуль chardet
    • 3.1 UTF-N с МПБ
    • 3.2 Экранированные кодировки
    • 3.3 Многобайтныйе кодировки
    • 3.4 Однобайтные кодировки
    • 3.5 windows-1252

Погружение

Вопрос: что является первой причины тарабарщины на страницах интернета, с вашем почтовом ящике, и в любой компьютерной системе когда либо написанной? Это кодировка символов. В главе про строки, я рассказывал о истории кодировок и создании Юникода, "одной кодировки правящей всеми". Я бы полюбил ее если бы никогда больше не видел тарабарщины в вебе, потому что все системы сохраняют верную информацию о кодировке, все протоколы передачи данных поддерживают Юникод, и каждая система работы с текстом придерживалась идеальной верности когда конвертировала из одной кодировки в другую.

Еще я люблю пони.

Пони юникода.

Юникодопони, так сказать.

Я остановлюсь на автоматическом определении кодировки символов.

Что такое автоматическое определение кодировки символов?

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

Разве это возможно?

Вообще, да. Однако, некоторые кодировки оптимизированны для конктерных языков, и языки не случайны. Некоторые последовательности символов встречаются постоянно, когда другие очень редки. Человек спокойно говорящий по английски открывая газету и найдя там “txzqJv 2!dasd0a QqdKjvz” сразу определит что это не английский(даже если состоит полностью из английских букв). При помощи изучения большого количества "обычного" текста, компьютерный алгоритм может имитировать знание языка и обучиться делать предположения о языке текста.

Другими словами, определение кодировки это на самом деле определение языка, объедененное со знанием какой язык использовать с какой кодировкой.

Такой алгоритм существует?

Черт побери да! Все основные браузеры имеют встроенное автоопределение кодировки, поскольку интернет полон страниц на которых кодировка вообще не указана. Mozilla Firefox включает библиотеку автоопределения кодировки симоволов с открытым исходным кодом. Я портировал ее на Python2 и продублировал в модуле chardet. Эта глава покажет вам пошагово весь процесс переноса модуля chardet с Python 2 до Python 3.

Введение в модуль chardet

Прежде чем начать переносить код, я помогу вам понять как этот код работает! Это краткий инструктаж по навигации в исходном коде. Библиотека chardet слишком болшьшая чтобы включать ее здесь полностью, но вы пожете скачать ее с chardet.feedparser.org.

Точкой входа алгоритма определения является universaldetector.py, в котором есть один класс UniversalDetector. (Вы могли подумать что точка входа это функция detect в chardet/__init__.py, но на самом деле это просто удобная функция которая создает объект UniversalDetector, вызывает его, и возвращает его результат)

Вот пять категорий кодировок которые поддерживает UniversalDetector:

  • UTF-N с меткой порядка байт(МПБ). Это включает в себя UTF-8, оба большой-индийский и малый-индийский вариант UTF-16, и все 4 варианта UTF-32 зависящих от порядка байт.
  • Экранированные последовательности, которые полностью совместимы с 7-битным ASCII, где символы не входящие в семибитную кодировку ASCII начинаются с символа экранирования. Например: ISO-2022-JP(Японская) и HZ-GB-2312(Китайская)
  • Многобайтовые кодировки, где каждый символ представлен различным количеством байт. Например: BIG5(Китайская), SHIFT_JIS(Японская), и TIS-620(Тайская)
  • Однобайтовые кодировки, где каждый символ представлен одним байтом. Нарпимер: KOI8-R(Русская), WINDOWS-1266(Иврит), и TIS-620(Тайская)
  • WINDOWS-1252, которая в основном используется в Microsoft Windows менеджерами среднего звена которые не хотят задумываться о кодировке символов сидя в своей норе.

UTF-N с МПБ

Если текст начинается с МПБ, мы можем разумно заключить что текст закодирован при помощи кодировки UTF-8, UTF-16, или UTF-32. (МБП расскажет нам какой именно; именно для этого она и служит.) Это поддерживается в UniversalDetector, который вернет результат сразу без каких-либо дальнейших изысканий.

Экранированные кодировки

Если текст содержит распознаваемую экранированную последовательность то это может быть индикатором экранированной кодировки, UniversalDetector создаст EscCharSetProber(определенный в escprober.py) и отдаст текст на обработку.

EscCharSetProber создаст ряд конечных автоматов, основанных на моделях HZ-GB-2312, ISO-2022-CN, ISO-2022-JP, и ISO-2022-KR (определенных в escsm.py). EscCharSetProber пропустит текст через каждый конечный автомат, побайтово. Если только один из автоматов даст положительный результат проверки, EscCharSetProber незамедлительно вернет его в UniversalDetector, который, в свою очередь, отдаст его вызвавшему его процессу. Если любой из конечных автоматов наткнется на недопустимую последовательность, он останавливается и далее продолжается обработка при помощи другого конечного автомата.

Многобайтныйе кодировки

Основываясь на МПБ, UniversalDetector проверяет содержит ли текст символы со старшим байтом. Если так, то он создает набор «исследователей» для определения многобайтных кодировок, однобайтных кодировок, и в качестве последнего средства windows-1252.

Исследователь для многобайтныйх кодировок, MBCSGroupProber(определенный в mbcsgroupprober.py), на самом деле просто консоль которая управляет группой других исследователей, по одному на каждую многобайтную кодировку: Big5, GB2312, EUC-TW, EUC-KR, EUC-JP, SHIFT_JIS, и UTF-8. MBCSGroupProber отдает текст каждому из этих кодировкозависимых исследователей и проверяет результат. Если исследователь сообщает что нашел недопустимую последовательность, он исключается из дальнейшей обработки(так что любые последующие вызовы UniversalDetector.feed() пропустят этого исследователя). Если исследователь сообщает что он достаточно уверен в том что определил кодировку, MBCSGroupProber сообщает о положительном результате в UniversalDetector, который передает результат вызвавшему его процессу.

Большинство исследователей многобайтных кодировок наследованы от MultiByteCharSetProber(определенном в mbcharsetprober.py) и просто включают в себя подходящий конечный автомат и анализатор распределения а остальную работу выполняет MultiByteCharSetProber. MultiByteCharSetProber пропускает текст через зависимый от кодировки конечный автомат побайтно, чтобы найти последовательность байт которая бы указала на положительный или отрицательный результат. В то же время, MultiByteCharSetProber пропускает текст через зависимый от кодировки анализатор распределения.

Анализатор распределения(каждый определен в chardistrubution.py) использует модель в которой указано в каком языке какие символы встречаются чаще. Как только MultiByteCharSetProber отдал достаточно текста для анализа, вычисляется рейтинг схожести основанный на числе часто используемых символов, общем количестве символов, и коэффициенте распределения специфичном для языка. Если уверенность достаточно высока, MultiByteCharSetProber возвращает результат в MBCSGroupProber, который возвращает результат в UniversalDetector, а он в свою очередь вызвавшему его процессу.

Тяжелее всего разобраться с японским. Односимвольного анализатора распределения не всегда достаточно чтобы различить EUC-JP и SHIFT_JIS, поэтому SJISProber(определенный в sjisprober.py) также использует двухсимвольный анализатор распределения. SJISContextAnalysis и EUCJPContextAnalysis (оба определенные в jpcntx.py и оба наследованные от класса JapaneseContextAnalysis) проверяют частоту повторения символов Хироганы в тексте. Как только достаточно текста было обработано, он возвращает уровень уверенности в SJISProber, который проверяет оба анализатора и возвращает результат на уровень выше в MBCSGroupProber.

Однобайтные кодировки

Серьезно, где мой пони юникода?

Исследователь для однобайтных кодировок, SBCSGroupProber(определенный в sbcsgroupprober.py), так же просто консоль которая управляет группой исследователей, по одному на каждую комбинацию однобайтной кодировки и языка: windows-1251, KOI8-R, ISO-8859-5, MacCyrillic, IBM855, и IBM866 (Русский); ISO-8859-7 и windows-1253 (Греческий); ISO-8859-5 и windows-1251 (Болгарский); ISO-8859-2 и windows-1250 (Венгерский); TIS-620 (Тайский); windows-1255 и ISO-8859-8 (Иврит).

SBCSGroupProber отдает текст каждому такому исследователю специфичному для языка и кодировки и проверяет результат. Все эти исследователи реализованы как один класс, SingleByteCharSetProber (определенный в sbcharsetprober.py), который принимает модель языка в качестве аргумента. Модель языка определяет как часто встречаются различные двухсимвольные последовательности в обычном тексте. SingleByteCharSetProber обрабатывает текст и отмечает наиболее часто используемые двухсимвольные последовательности. Как только было обработано достаточно текста, он вычисляет уровень схожести основанный на количестве часто встречающихся последовательностей, общем количестве символов, и специфичным для языка коэффициентом распределения.

Иврит обрабатывается по особому. Если при помощи анализа двухсимвольного распределения выясняется что текст может быть на Иврите, HebrewProber(определенный в hebrewprober.py) пробует различить Визуальный Иврит(где каждая строка исходного текста хранится «наоборот», и потом отображается так же чтобы она могла быть прочтена с права на лево) и Логический Иврит(где текст сохранен в порядке чтения и после этого отображается в клиенте справа на лево). Поскольку некоторые символы кодируются различно в зависимости от положения в слове, мы можем сделать обоснованное предположение о направлении исходного текста, и определить нужную кодировку(windows-1255 для Логического Иврита или ISO-8859-8 для Визуального Иврита)

Windows-1252

Если UniversalDetector определяет символы со старшим байтом в тексте, но ни один из других многобайтныйх или однобайтный исследователей не вернул положительный результат, создается Latin1Prober(определенный в latin1prober.py) чтобы попытаться определить английский текст в кодировке windows-1252. Это будет изначально не надежным анализом, потому что английские символы закодированы таким же способом как и во многих различных кодировках. Единственный способ определить windows-1252 это обратить внимание на часто используемые символы как умные кавычки, вьющиеся апострофы, символы копирайта и т. д. Latin1Prober автоматически уменьшает свой уровень уверенности чтобы другие, более достоверные, исследователи могли выиграть если возможно.

Создание пакетов библиотек»

Перенос кода на Python 3 с помощью 2to3»

Особые названия методов

 

Мы уже обнаружили несколько специальных наименований методов повсюду в книге — магические методы которые питон вызывает когда вы используете определенный синтаксис. Используя специальные методы ваши классы могут работать как последовательности, как словари, как функции, как итераторы или даже как числа. Аппендикс служит как справочник по специальным методам которые мы уже видели и короткое введение в некоторые более эзотерические из них.

Основы

Если вы читали введение в классы вы уже видели самые общие специальные методы: метод __init__(). Многообразие классов которые я написал требуют некоторой инициализации. Также есть некоторые другие специальные методы которые особенно полезны для отлаживания ваших пользовательских классов.

  1. Метод __init__() вызывается после того как экземпляр создан. Если вы хотите контролировать процесс создания используйте метод __new__().
  2. По соглашению метод __repr__() должен возвращать строку которая является действительным питоновским выражением.
  3. Метод __str__() также вызывается когда используется print(x).
  4. Новое в Python3, был введен новый тип bytes.
  5. По соглашению, format_spec должен удовлетворять Format Specification Mini-Language decimal.py в стандартной библиотеке Python в которой имееться свой метод __format__().

Классы, которые ведут себя как итераторы.

В главе про итераторы вы видели как построить итератор с нуля используя методы __iter__() и __next__().

  1. Метод __iter__() вызываеться когда вы создаете новый итератор. Это хорошее место для инициализации итератора начальными значениями.
  2. Метод __next__() вызываеться когда вы получаете следующее значение из итератора.
  3. Метод __reversed__() является . Он получает существующую последовательность и возвращает итератор который производит элементы в последовательности в обратном порядке, от последнего к первому.

Как вы видели в главе Итераторы, цикл for может быть применен к итератору. В этом цикле:

for x in seq:
print(x)

Python 3 будет вызывать seq.__iter__() для создания итератора, затем вызовет метод __next__() для этого итератора для получения каждого значения х.

Когда метод __next__()

Куда пойти

 

Это стоит прочитать

Существует некоторое количество тем недостаточно раскрытых в этой книге, однако для их раскрытия существуют открытые ресурсы.

Декораторы:

  • Декораторы Функций от Ariel Ortiz
  • Подробнее о Декораторах Функций от Ariel Ortiz
  • Очаровательный Python: Магия Декораторов это просто от David Mertz
  • Определение Функций в официальной документации Python

Свойства:

  • The Python propertybuiltin от Adam Gomaa
  • Getters/Setters/Fuxors от Ryan Tomayko
  • property()функция в официальной документации Python

Дескрипторы:

  • How-To руководство по Дескрипторам от Raymond Hettinger
  • Очаровательный Python: Элегантность и недостатки Python, Часть 2 от David Mertz
  • Дескрипторы Python от Mark Summerfield
  • Вызов Дескрипторов в официальной документации Python

Мультипотоковость & многопроцессорность:

  • threadingмодуль
  • threading Управление конкурирующими потоками
  • multiprocessingмодуль
  • multiprocessing Управление процессами как потоками
  • Потоки Python и Global Interpreter Lock от Jesse Noller
  • Внутри Python GIL (видео) от David Beazley

Метаклассы:

  • Программирование метаклассов в Python от David Mertz and Michele Simionato
  • Программирование метаклассов в Python, Часть 2 от David Mertz and Michele Simionato
  • Программирование метаклассов в Python, Часть 3 от David Mertz and Michele Simionato

И в дополнение Doug Hellman™ Python Модуль недели, это фантастическое руководство к большинству модулей для стандартной библиотки Python.