Как установить кодировку sys.stdout в Python 3?
Установка выходной кодировки по умолчанию в 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 ответов
Начиная с Python 3.7 вы можете изменить кодировку стандартных потоков с помощью reconfigure()
:
sys.stdout.reconfigure(encoding='utf-8')
Вы также можете изменить способ обработки ошибок кодирования, добавив errors
параметр.
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())
Я нашел эту тему при поиске решений той же ошибки,
Альтернативное решение по сравнению с уже предложенными - установить PYTHONIOENCODING
переменная окружения перед запуском Python, для моего использования - это меньше проблем, чем замена sys.stdout
после инициализации Python:
PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py
С преимуществом не нужно идти и редактировать код Python.
Другие ответы рекомендуют использовать 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"
,
Установка выходной кодировки по умолчанию в Python 2 - это хорошо известная идиома
Ик! Это известная идиома в Python 2? Это выглядит как опасная ошибка для меня.
Это, безусловно, испортит любой скрипт, который пытается записать двоичный файл в stdout (который вам понадобится, если вы, например, CGI-скрипт, возвращающий изображение). Байты и символы - совершенно разные животные; Это не очень хорошая идея, чтобы обезопасить интерфейс, который указан для приема байтов, с тем, который принимает только символы.
CGI и HTTP вообще явно работают с байтами. Вы должны только отправлять байты в sys.stdout. В Python 3 это означает использование sys.stdout.buffer.write
отправлять байты напрямую. Кодирование содержимого страницы в соответствии с его charset
Параметр должен обрабатываться на более высоком уровне в вашем приложении (в случаях, когда вы возвращаете текстовое содержимое, а не двоичное). Это также означает print
больше не годится для компьютерной графики.
(Чтобы добавить к путанице, CGIHandler wsgiref был сломан в py3k до недавнего времени, что делает невозможным развертывание WSGI в CGI таким образом. С PEP 3333 и Python 3.2 это наконец-то работоспособно.)
С помощью 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
вместо стандартного выхода.)
sys.stdout находится в текстовом режиме в Python 3. Следовательно, вы пишете в него юникод напрямую, и идиома для 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.