Кодировка текстового содержимого: использование информации в заголовках для декодирования

kodirovka tekstovogo soderzhimogo ispolzovanie informacii v zagolovkah dlya dekodirovaniya Сценарии на стороне клиента

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

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

>  >> s = b’A\xe4B’

>  >> s.decode(‘latin1’)

‘A.a.B’

>  >> from email.message import Message

>  >> m = Message()

>  >> m.set_payload(b’A\xe4B’, charset=’latin1′) # или ‘latin-1’: см. далее

>  >> t = m.as_string()

>  >> print(t)

MIME-Version: 1.0

Content-Type: text/plain; charset="latin1"

Content-Transfer-Encoding: base64

QeRC

>  >> m.get_content_charset() ‘latin1’

Обратите внимание, что пакет email автоматически применяет MIME- кодирование Base64 к частям с текстом, который не является текстом ASCII, чтобы обеспечить соответствие стандартам электронной почты. То же самое справедливо для более специализированных подклассов с поддержкой преобразования в формат MIME класса Message:

>  >> from email.mime.text import MIMEText

>  >> m = MIMEText(b’A\xe4B’, _charset=’latin1′)

>  >> t = m.as_string()

>  >> print(t)

Content-Type: text/plain; charset="latin1"

MIME-Version: 1.0

Content-Transfer-Encoding: base64

QeRC

>   >> m.get_content_charset() ‘latin1’

Если теперь проанализировать текст этого сообщения с помощью пакета email, мы получим новый объект Message, содержимое которого в формате MIME Base64 представляет строку Юникода с символами не из диапазона ASCII. Если затребовать содержимое с декодированием из формата MIME, указав аргумент decode=1, будет возвращена строка байтов, которую мы ранее вложили в сообщение:

>   >> from email.parser import Parser

>>> q = Parser().parsestr(t)

>>> q

<email.message.Message object at 0x019ECA50>

>>> q.get_content_type()

‘text/plain’

>>> q._payload

‘QeRC\n’

>>> q.get_payload()

‘QeRC\n’

>   >> q.get_payload(decode=1)

b’A\xe4B’

Однако попытка получить текст декодированием этой строки байтов с использованием кодировки по умолчанию в Windows (UTF8) потерпит неудачу. Для большей точности и поддержки большего разнообразия типов текста необходимо использовать информацию о кодировках, сохраненную при анализе и прикрепленную к объекту Message. Это особенно важно, когда может возникнуть необходимость сохранить данные в файл — мы должны будем либо сохранять данные в двоичном режиме, либо указывать корректную (или, по крайней мере, совместимую) кодировку, чтобы сохранять такие строки в текстовых файлах. Декодирование вручную выполняется точно так же:

>   >> q.get_payload(decode=1).decode()

UnicodeDecodeError: ‘utf8’ codec can’t decode bytes in position 1-2: unexpected

(UnicodeDecodeError: кодек ‘utf8’ не может декодировать байты

в позиции 12: неожиданный…)

>   >> q.get_content_charset()

‘latin1’

>   >> q.get_payload(decode=1).decode(‘latin1’) # известный тип

‘A.a.B’

>   >> q.get_payload(decode=1).decode(q.get_content_charset()) # для любого

# типа

A.a.B

На самом деле, в объектах Message имеется информация обо всех заголовках, нужно только знать, как ее найти. Информация о кодировке может вообще отсутствовать, и в этом случае возвращается значение None. Клиенты должны предусматривать политику обращения с таким неоднозначным текстом (они могут попытаться применить наиболее распространенные кодировки, определить кодировку по содержимому или обращаться с данными, как с простой строкой байтов):

>>> q[‘content-type’] # интерфейс отображения

‘text/plain; charset="latin1"’

>>> q.items()

[(‘Content-Type’, ‘text/plain; charset="latin1"’), (‘MIME-Version’, ‘1.0’), (‘Content-Transfer-Encoding’, ‘base64’)]

>> q.get_params(header=’Content-Type’) # интерфейс параметров

[(‘text/plain’, »), (‘charset’, ‘latin1’)]

>>> q.get_param(‘charset’, header=’Content-Type’)

‘latin1’

>>> charset = q.get_content_charset() # информация может отсутствовать

>>> if charset:

print(q.get_payload(decode=1).decode(charset))

A.a.B

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

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

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

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