Кодировка текстового содержимого: обработка результатов смешанного типа

kodirovka tekstovogo soderzhimogo obrabotka rezultatov smeshannogo tipa Сценарии на стороне клиента

Следующая проблема в пакете email, связанная с поддержкой Юникода, в некоторой степени даже противоречит модели программирования на языке Python в целом: типы данных содержимого объектов сообщений могут отличаться в зависимости от того, как они извлекаются. Это особенно усложняет реализацию программ, которые выполняют обход и обработку частей сообщения с содержимым.

В частности, метод get_payload объекта Message, который мы использовали выше, принимает необязательный аргумент decode, управляющий автоматическим декодированием данных в формате MIME (например, Base64, uuencode, quoted-printable). Если в этом аргументе передать число 1 (или True), содержимое будет декодироваться при извлечении, если это необходимо. Это настолько удобно при обработке сложных сообщений с произвольными частями, что в этом аргументе обычно всегда передается значение 1 . Двоичные части сообщения, как правило, всегда кодируются в формат MIME, но даже текстовые части могут быть представлены в одном из форматов MIME, таком как Base64, если значения их байтов выходят за рамки стандартов электронной почты. Некоторые типы текста Юникода, например, требуют кодирования в формат MIME.

В результате метод get_payload, обычно возвращающий строки str для текстовых частей str, будет возвращать строки bytes, если его аргумент decode имеет значение True — даже когда заранее известно, что обрабатываемая часть сообщения по своей природе является текстовой. Если этот аргумент не используется, тип содержимого будет зависеть от его исходного типа: str или bytes. Поскольку в Python 3.X не допускается смешивать типы str и bytes в операциях, клиенты, которые предусматривают сохранение результатов в файлах или их обработку как текста, должны учитывать эти различия. Выполним некоторый программный код для иллюстрации:

>>> from email.message import Message

>>> m = Message()

>>> m[‘From’] = ‘Lancelot’

>>> m.set_payload(‘Line?…’)

>>> m[‘From’]

‘Lancelot’

>>> m.get_payload() # str, если содержимое имеет тип str

‘Line?…’

>>> m.get_payload(decode=1) # bytes, (то же, что и decode=True) выполняется

bLine?…’ # декодирование MIME, если необходимо

Сочетание этих различий в типах возвращаемых данных со строгим разделением типов str/bytes в Python 3.X может вызывать проблемы обработки результатов при невнимательном отношении к декодированию:

>>> m.get_payload(decode=True) + ‘spam’ # нельзя смешивать в 3.X!

TypeError: can’t concat bytes to str

(TypeError: невозможно объединить bytes и str)

>>> m.get_payload(decode=True).decode() + ‘spam’ # преобразовать,

Line?spam # если необходимо

Чтобы вы могли понять смысл этих примеров, запомните, что к тексту сообщения электронной почты применяются два разных понятия термина «кодировка»:

     Ко диров ки MIME элек трон ной поч ты, такие как Base64, uuencode и quoted-printable, применяются к двоичным данным и другому необычному содержимому, чтобы обеспечить возможность их передачи в тексте сообщения электронной почты.

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

Пакет email обрабатывает кодировки MIME автоматически, когда при анализе и извлечении содержимого указывается аргумент decode=1 или когда создается сообщение, содержащее непечатаемые части, однако сценарии по-прежнему должны принимать во внимание кодировки Юникода из-за важных отличий между строковыми типами в Python 3.X. Например, следующая инструкция выполняет первое декодирование из формата MIME, а второе — в строку символов Юникода:

m.get_payload(decode=True).decode() # в bytes через MIME, затем в str

Даже когда аргумент decode не используется, тип содержимого все равно может отличаться при сохранении в разных форматах:

>>> m = Message(); m.set_payload(‘spam’); m.get_payload() # извлекаемый тип

‘spam’ # соответствует

>>> m = Message(); m.set_payload(b’spam’); m.get_payload() # сохраненному

bspam # типу

То же самое справедливо для текстовых подклассов MIME (хотя, как мы увидим далее в этом разделе, мы не можем передавать строки bytes их конструкторам, чтобы принудительно создать двоичное содержимое):

>>> from email.mime.text import MIMEText

>>> m = MIMEText(‘Line…?’)

>>> m[‘From’] = ‘Lancelot’

>>> m[‘From’]

‘Lancelot’

>>> m.get_payload()

‘Line…?’

>>> m.get_payload(decode=1)

bLine…?’

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

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий