如何在Python 3中设置sys.stdout编码?

时间:2010-12-07 07:59:35

标签: python unicode python-3.x stdout

在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 个答案:

答案 0 :(得分:38)

Python 3.1添加了io.TextIOBase.detach(),并在sys.stdout的文档中添加了注释:

  

默认情况下,标准流处于文本模式。要将二进制数据写入或读取,请使用基础二进制缓冲区。例如,要将字节写入stdout,请使用sys.stdout.buffer.write(b'abc')。默认情况下,可以将io.TextIOBase.detach()流用作二进制。此函数将stdinstdout设置为二进制:

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())

答案 1 :(得分:27)

我在搜索同一错误的解决方案时找到了这个帖子,

对于那些已经建议的解决方案的替代解决方案是在 Python启动之前设置PYTHONIOENCODING环境变量供我使用 - 这比在Python之后交换sys.stdout更麻烦。初始化:

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py

具有无需编辑Python代码的优点。

答案 2 :(得分:25)

其他答案似乎建议使用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"运行它也能正常工作。

答案 3 :(得分:17)

  

在Python 2中设置默认输出编码是一个众所周知的习惯用法

伊克!这是Python 2中一个众所周知的习惯用法吗?这对我来说似乎是一个危险的错误。

它肯定会搞乱任何试图将二进制文件写入stdout的脚本(例如,如果你是一个返回图像的CGI脚本,你将需要它)。字节和字符是完全不同的动物;将指定接受字节的接口修补为仅接受字符的接口不是一个好主意。

CGI和HTTP通常明确地使用字节。您应该只向sys.stdout发送字节。在Python 3中,这意味着使用sys.stdout.buffer.write直接发送字节。编码页面内容以匹配其charset参数应在应用程序的更高级别处理(在您返回文本内容而不是二进制文件的情况下)。这也意味着print不再对CGI有利。

(更令人困惑的是,wsgiref的CGIHandler在py3k中被破坏,直到最近,这使得无法以这种方式将WSGI部署到CGI。使用PEP 3333和Python 3.2,这终于可行了。)

答案 4 :(得分:13)

从Python 3.7开始,您可以使用reconfigure()更改标准流的编码:

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

您还可以通过添加errors参数来修改如何处理编码错误。

答案 5 :(得分:10)

使用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。)

答案 6 :(得分:8)

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的构建中。