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