Как установить кодировку sys.stdout в Python 3? - PullRequest
53 голосов
/ 07 декабря 2010

Установка выходной кодировки по умолчанию в Python 2 является широко известной идиомой:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

Это оборачивает объект sys.stdout в устройство записи кодеков, которое кодирует выходные данные в UTF-8.

Однако этот метод не работает в Python 3, поскольку sys.stdout.write() ожидает str, но результат кодирования равен bytes, и возникает ошибка, когда codecs пытается записать закодированные байты в исходный sys.stdout.

Как правильно сделать это в Python 3?

Ответы [ 7 ]

38 голосов
/ 07 декабря 2010

Добавлен Python 3.1 io.TextIOBase.detach(), с примечанием в документации для sys.stdout:

Стандартные потоки по умолчанию находятся в текстовом режиме.Чтобы записать или прочитать двоичные данные в них, используйте базовый двоичный буфер.Например, чтобы записать байты в stdout, используйте sys.stdout.buffer.write(b'abc').Использование io.TextIOBase.detach() потоков может быть сделано двоичным по умолчанию.Эта функция устанавливает двоичные значения stdin и stdout:

def make_streams_binary():
    sys.stdin = sys.stdin.detach()
    sys.stdout = sys.stdout.detach()

Следовательно, соответствующая идиома для Python 3.1 и более поздних версий:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
27 голосов
/ 23 октября 2011

Я нашел эту ветку при поиске решений той же ошибки,

Альтернативное решение, которое уже было предложено, - установить переменную окружения PYTHONIOENCODING до . Запуск Python для моегоиспользуйте - это меньше проблем, чем замена sys.stdout после инициализации Python:

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py

С преимуществом, что вам не нужно идти и редактировать код Python.

25 голосов
/ 02 ноября 2015

Другие ответы рекомендуют использовать codecs, но open работает для меня:

import sys
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)
print("日本語")
# Also works with other methods of writing to stdout:
sys.stdout.write("日本語\n")
sys.stdout.buffer.write("日本語\n".encode())

Это работает, даже когда я запускаю его с PYTHONIOENCODING="ascii".

17 голосов
/ 07 декабря 2010

Установка выходной кодировки по умолчанию в Python 2 - это хорошо известная идиома

Eek!Это известная идиома в Python 2?Мне это кажется опасной ошибкой.

Это наверняка испортит любой скрипт, который пытается записать двоичный файл в стандартный вывод (например, вам понадобится, если вы CGI-скрипт, возвращающий изображение),Байты и символы - совершенно разные животные;не очень хорошая идея, чтобы обезопасить интерфейс, который определен для приема байтов, с тем, который принимает только символы.

CGI и HTTP вообще явно работают с байтами.Вы должны только отправлять байты в sys.stdout.В Python 3 это означает использование sys.stdout.buffer.write для прямой отправки байтов.Кодирование содержимого страницы в соответствии с ее параметром charset должно обрабатываться на более высоком уровне в вашем приложении (в случаях, когда вы возвращаете текстовое содержимое, а не двоичное).Это также означает, что print больше не годится для CGI.

(Чтобы добавить к путанице, CGIHandler wsgiref был сломан в py3k до недавнего времени, что делает невозможным развертывание WSGI в CGI таким образом.PEP 3333 и Python 3.2, наконец, это выполнимо.)

15 голосов
/ 17 сентября 2018

Начиная с Python 3.7, вы можете изменить кодировку стандартных потоков с помощью reconfigure():

sys.stdout.reconfigure(encoding='utf-8')

Вы также можете изменить способ обработки ошибок кодирования, добавив параметр errors.

10 голосов
/ 05 июня 2015

Использование detach() заставляет интерпретатора вывести предупреждение, когда он пытается закрыть стандартный вывод непосредственно перед выходом:

Exception ignored in: <_io.TextIOWrapper mode='w' encoding='UTF-8'>
ValueError: underlying buffer has been detached

Вместо этого у меня все работало нормально:

default_out = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')

(И, конечно, запись в default_out вместо стандартного вывода.)

8 голосов
/ 07 декабря 2010

sys.stdout находится в текстовом режиме в Python 3. Следовательно, вы пишете Unicode напрямую, и идиома для Python 2 больше не нужна.

Где это не получится в Python 2:

>>> import sys
>>> sys.stdout.write(u"ûnicöde")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfb' in position 0: ordinal not in range(128)

Однако в Python 3 все работает просто отлично:

>>> import sys
>>> sys.stdout.write("Ûnicöde")
Ûnicöde7

Теперь, если ваш Python не знает, какова ваша кодировка stdouts, это другая проблема, скорее всего, при сборке Python.

...