Python по умолчанию / неявные строковые кодировки

Когда, где и как Python неявно применяет кодирование к строкам или неявное транскодирование (преобразование)?

И что это за "стандартные" (то есть подразумеваемые) кодировки?

Например, каковы кодировки:

  • строковых литералов?

    s = "Byte string with national characters"
    us = u"Unicode string with national characters"
    
  • байтовых строк при преобразовании типов в и из Unicode?

    data = unicode(random_byte_string)
    
  • когда байтовые и юникодные строки записываются в / из файла или терминала?

    print(open("The full text of War and Peace.txt").read())
    

2 ответа

Решение

Здесь задействовано несколько частей функциональности Python: чтение исходного кода и анализ строковых литералов, транскодирование и печать. У каждого свои условности.

Короткий ответ:

  • Для разбора кода:
    • str (Py2) - не применимо, сырые байты из файла берутся
    • unicode (Py2) / str (Py3) - "кодировка источника", по умолчанию ascii (Py2) и utf-8 (PY3)
    • bytes (Py3) - нет, символы не-ascii запрещены в буквальном
  • Для транскодирования:
    • оба (Py2) - sys.getdefaultencoding() (ascii почти всегда)
      • существуют неявные преобразования, которые часто приводят к UnicodeDecodeError / UnicodeEncodeError
    • оба (Py3) - нет, должны явно указывать кодировку при конвертации
  • Для целей ввода / вывода:
    • unicode (Py2) - <file>.encoding если установлено, иначе sys.getdefaultencoding()
    • str (Py2) - не применимо, записываются необработанные байты
    • str (Py3) - <file>.encoding всегда установлен и по умолчанию locale.getpreferredencoding()
    • bytes (Py3) - нет, print Инг производит его repr() вместо

Прежде всего, некоторые уточнения терминологии, чтобы вы правильно поняли все остальное. Декодирование - это перевод байтов в символы (Unicode или другие), а кодирование (как процесс) - обратное. См. "Абсолютный минимум". Каждый разработчик программного обеспечения должен абсолютно точно знать о юникоде и наборах символов (без извинений!).

Сейчас...

Чтение источника и разбор строковых литералов

В начале исходного файла вы можете указать "исходную кодировку" файла (точный эффект описан ниже). Если не указано, по умолчанию ascii для Python 2 и utf-8 для Python 3. Спецификация UTF-8 имеет тот же эффект, что и utf-8 декларация кодировки.

Python 2

Python 2 читает исходный код как необработанные байты. Он использует "исходную кодировку" только для анализа литерала Unicode, когда он его видит. ( Это сложнее, чем под капотом, но это чистый эффект.)

> type t.py
#encoding: cp1251
s = "абвгд"
us = u"абвгд"
print repr(s), repr(us)
> py -2 t.py
'\xe0\xe1\xe2\xe3\xe4' u'\u0430\u0431\u0432\u0433\u0434'

<change encoding declaration in the file to cp866, do not change the contents>
> py -2 t.py
'\xe0\xe1\xe2\xe3\xe4' u'\u0440\u0441\u0442\u0443\u0444'

<transcode the file to utf-8, update declaration or replace with BOM>
> py -2 t.py
'\xd0\xb0\xd0\xb1\xd0\xb2\xd0\xb3\xd0\xb4' u'\u0430\u0431\u0432\u0433\u0434'    

Таким образом, обычные строки будут содержать точные байты, которые находятся в файле. И строки Unicode будут содержать результат декодирования байтов файла с помощью "кодировки источника".

Если расшифровка не удалась, вы получите SyntaxError, То же самое, если в файле есть символ, отличный от ascii, когда кодировка не указана. Наконец, если unicode_literals Будущее используется, любые обычные строковые литералы (только в этом файле) при синтаксическом анализе обрабатываются как литералы Unicode, со всеми этими значениями.

Python 3

Python 3 декодирует весь исходный файл с "исходной кодировкой" в последовательность символов Unicode. Любой разбор делается после этого. (В частности, это позволяет использовать Unicode в идентификаторах.) Поскольку все строковые литералы теперь являются Unicode, никакого дополнительного транскодирования не требуется. В байтовых литералах не-ascii символы запрещены (такие байты должны быть указаны с escape-последовательностями), что полностью исключает проблему.

транскодирование

Согласно разъяснениям в начале:

  • str - bytes => может быть только decode d (то есть непосредственно; подробности следуют)
  • unicode - символы => могут быть только encode d

Python 2

В обоих случаях, если кодировка не указана, sys.getdefaultencoding() используется. это ascii (если вы не раскомментируете фрагмент кода в site.py или сделать несколько других взломов, которые являются рецептом для катастрофы). Итак, с целью транскодирования, sys.getdefaultencoding() является "кодировкой строки по умолчанию".

Теперь вот предостережение:

  • decode() а также encode() - с кодировкой по умолчанию - выполняется неявно при преобразовании str<->unicode :

    • в форматировании строки (треть UnicodeDecodeError / UnicodeEncodeError вопросы по ТАМ об этом)
    • при попытке encode() str или же decode() unicode (2-я треть вопросов SO)

Python 3

Там нет "кодировки по умолчанию" вообще: неявное преобразование между str а также bytes сейчас запрещено.
(Как показывает число вопросов SO от запутанных пользователей, это оказалось более трудным, чем оно того стоит.)

  • bytes может быть только decode д и str - encode д, и encoding аргумент обязателен.
  • преобразование bytes->str (в том числе неявно) производит его repr() вместо этого (что полезно только для печати), полностью избегая проблемы с кодировкой
  • преобразование str->bytes запрещено

печать

Этот вопрос не связан со значением переменной, но связан с тем, что вы увидите на экране, когда она print ed - и получишь ли ты UnicodeEncodeError когда print ING.

Python 2

  • unicode является encode д с <file>.encoding если установлено; в противном случае он неявно преобразуется в str в соответствии с вышеизложенным. (Последняя треть UnicodeEncodeError ТАК вопросы падают здесь.)
    • Для стандартных потоков кодировка потока определяется при запуске из различных источников, зависящих от среды, и может быть переопределена с помощью PYTHONIOENCODING envvar.
  • str байты отправляются в поток ОС как есть. Какие конкретные символы вы увидите на экране, зависит от кодировки вашего терминала (если это что-то вроде UTF-8, вы можете вообще ничего не увидеть, если напечатаете последовательность байтов, которая является недействительной UTF-8).

Python 3

Изменения:

  • Сейчас file s открыт с текстом против двоичного mode изначально принять str или же bytes Соответственно и прямо отказываются обрабатывать не тот тип. Файлы текстового режима всегда имеют encoding задавать, locale.getpreferredencoding(False) быть по умолчанию.
  • print для текстовых потоков все еще неявно преобразует все в str который в случае bytes печатает его repr() в соответствии с вышеизложенным, уклонение от проблемы кодирования в целом

Неявное кодирование как внутренний формат для хранения строк / массивов: вам не нужно заботиться о кодировке. Фактически Python декодирует символы внутренним способом Python. Это в основном прозрачно. Просто представьте, что это текст в кодировке Unicode или последовательность байтов абстрактным образом.

Внутренняя кодировка в Python 3.x варьируется в зависимости от "большего" символа. Это может быть UTF-8/ASCII (для строк ASCII), UTF-16 или UTF-32. Когда вы используете строки, это как если бы у вас была строка Unicode (такая абстрактная, а не настоящая кодировка). Если вы не программируете на C или используете некоторые специальные функции (просмотр памяти), вы никогда не сможете увидеть внутреннюю кодировку.

Байты - это просто представление реальной памяти. Python интерпретирует как unsigned char, Но опять же, часто вы должны просто думать о последовательности, а не о внутренней кодировке.

Python2 имеет байты и строку в виде знака без знака и юникод как UCS-2 (поэтому кодовые точки выше 65535 будут кодироваться с 2 символами (UCS2) в Python2 и только одним символом (UTF-32) в Python3)

Другие вопросы по тегам