Python 3 - если строка содержит только ASCII, равна ли она строке в виде байтов?

Рассмотрим Python 3 SMTPD - полученные данные содержатся в строке. http://docs.python.org/3.4/library/smtpd.html quote: "и данные - это строка, содержащая содержимое электронной почты"

Факты (исправить?):

  • Строки в Python 3 являются Unicode.
  • Письма всегда ASCII.
  • Pure ASCII действителен Unicode.

Поэтому электронное письмо, которое входит в систему, является чистым ASCII (который является действительным Unicode), поэтому строка SMTPD DATA в точности эквивалентна исходным байтам, полученным SMPTD. Правильно ли это?

Таким образом, мой вопрос: если я декодирую строку SMTPD DATA в ASCII или преобразую строку DATA в байты, это эквивалентно байтам фактического сообщения электронной почты, которое поступает через SMTP?

Контекст (и, возможно, лучший вопрос): "Как сохранить файл Python 3 SMTPD DATA как ТОЧНО получаемые байты?" Я обеспокоен тем, что, когда DATA переходит через преобразование строки в байты, он каким-то образом был изменен с исходных байтов, которые поступают через SMTP.

EDIT: кажется, разработчики Python считают, что SMTPD должен в любом случае возвращать двоичные данные. Кажется, что не было исправлено... http://bugs.python.org/issue19662

Ответ 1

если строка содержит только ASCII, равна ли она строке в виде байтов?

Нет. Он не равен в Python 3:

>>> '1' == b'1'
False

bytes объект не равен объекту str (строка Unicode) аналогичным образом, когда целое число не равно строке:

>>> '1' == 1
False

В некоторых языках программирования приведенные выше сравнения верны, например, в Python 2:

>>> b'1' == u'1'
True

и 1 == '1' в Perl:

$ perl -e "print qq(True\n) if 1 == q(1)"
True

Ваш вопрос - хороший пример того, почему более предпочтительное поведение Python 3 предпочтительнее. Это заставляет программистов противостоять ошибочным представлениям их текста/байта, не дожидаясь, когда их код сломается для некоторого ввода.


  • Строки в Python 3 являются Unicode.

да. Строки являются неизменяемыми последовательностями кодовых точек Unicode в Python 3.

  • Письма всегда ASCII.

Большинство писем переносятся как 7-битные сообщения (диапазон ASCII: hex 00-7F). Хотя "практически все современные почтовые серверы имеют 8-битную чистоту" ., то есть 8-битное содержимое не будет повреждено. И расширение 8BITMIME санкционирует передачу некоторого 8-битного контента.

Другими словами: электронные письма не всегда ASCII.

  • Pure ASCII действителен Unicode.

ASCII - кодировка символов. Вы можете декодировать некоторые последовательности байтов в Unicode с использованием кодировки символов US-ASCII. Строки Unicode не имеют связанной кодировки символов, то есть вы можете кодировать их в байтах, используя любую кодировку символов, которая может представлять соответствующие кодовые точки Unicode.

Поэтому электронное письмо, которое входит в систему, является чистым ASCII (который является действительным Unicode), поэтому строка SMTPD DATA в точности эквивалентна исходным байтам, полученным SMPTD. Правильно ли это?

Если вход находится в диапазоне ascii, тогда data.decode('ascii', 'strict').encode('ascii') == data. Хотя Lib/smtpd.py выполняет некоторые преобразования во входные данные (согласно RFC 5321), поэтому контент, который вы получаете как data, может быть другим даже если вход является чистым ASCII.


"Как сохранить файл Python 3 SMTPD DATA как ТОЧНО получаемые байты?"

Моя цель состоит не в том, чтобы найти неправильные электронные письма, а для того, чтобы сохранить входящие электронные письма на диск именно в форме бинарных/байтов, которые они прибыли.

Ошибка, с которой вы связались (smtpd.py не должен декодировать utf-8) делает smptd.py не 8-битным чистым.

Вы можете переопределить метод SMTPChannel.collect_incoming_data из smtpd.py, чтобы сохранить входящие байты как есть.


Строка текста ASCII также действительна в тексте UTF-8.

Это правда. Это приятное свойство кодировки UTF-8. Если вы можете декодировать последовательность байтов в Юникоде, используя кодировку символов US-ASCII, вы также можете декодировать байты с использованием кодировки символов UTF-8 (и получающиеся в результате кодовые точки Юникода в обоих случаях одинаковы).

smptd.py должен был использовать либо latin1 (он декодирует любую последовательность байтов), либо ascii (с "строгим" обработчиком ошибок для отказа в любом байте без ascii) вместо utf-8 (он позволяет некоторым не -ascii bytes - bad).

Имейте в виду:

  • некоторые электронные письма могут иметь байты вне диапазона ascii
  • de-transparent в соответствии с RFC 5321 не сохраняет входные байты как есть, даже если они все находятся в диапазоне ascii