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

Gre*_*ill 56 python unicode stdout python-3.x

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

sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
Run Code Online (Sandbox Code Playgroud)

这将sys.stdout对象包装在编解码器编写器中,该编解码器编写器以UTF-8编码输出.

但是,这种技术在Python 3中不起作用,因为sys.stdout.write()期望a str,但是编码的结果是bytes,并且当codecs尝试将编码的字节写入原始时发生错误sys.stdout.

在Python 3中执行此操作的正确方法是什么?

Gre*_*ill 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()
Run Code Online (Sandbox Code Playgroud)

因此,Python 3.1及更高版本的相应习语是:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
Run Code Online (Sandbox Code Playgroud)

  • 我会用'PYTHONIOENCODING`; 否则`io.TextIOWrapper`可能比`codecs`更好地替代正确处理新行. (5认同)
  • 这完全改变了sys.stdout的行为。由codecs.getwriter返回的StreamWriter不再是行缓冲的,例如。 (2认同)

ide*_*n42 29

我在搜索相同错误的解决方案时发现了这个帖子,

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

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py
Run Code Online (Sandbox Code Playgroud)

具有不必去编辑Python代码的优点.

  • 拇指上升主要是因为PYTHONIOENCODING = utf-8经过几个小时的搜索后解决了我的问题. (4认同)

Jac*_*nor 27

其他答案似乎建议使用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())
Run Code Online (Sandbox Code Playgroud)

即使我运行它也可以PYTHONIOENCODING="ascii".


sth*_*sth 21

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

sys.stdout.reconfigure(encoding='utf-8')
Run Code Online (Sandbox Code Playgroud)

您还可以通过添加errors参数来修改编码错误的处理方式.

  • @Dan 那么你就不能使用这个 (4认同)
  • 如果您想保持与 Python 3.6 的兼容性怎么办? (2认同)
  • @Dan 那么在这个页面上有很多其他答案和替代解决方案。我的答案不是这个问题的唯一答案,在 Python 3.7 之前还有其他方法的其他答案。这不是您要找的吗? (2认同)

bob*_*nce 17

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

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

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

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

(为了增加混乱,wsgiref的CGIHandler直到最近才在py3k中被破坏,使得无法以这种方式将WSGI部署到CGI.使用PEP 3333和Python 3.2,这终于可行了.)


pto*_*ato 10

使用detach()导致解释器在它退出之前尝试关闭stdout时打印警告:

Exception ignored in: <_io.TextIOWrapper mode='w' encoding='UTF-8'>
ValueError: underlying buffer has been detached
Run Code Online (Sandbox Code Playgroud)

相反,这对我来说很好:

default_out = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
Run Code Online (Sandbox Code Playgroud)

(当然,写作default_out代替stdout.)


Len*_*bro 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)
Run Code Online (Sandbox Code Playgroud)

但是,它在Python 3中只是花花公子:

>>> import sys
>>> sys.stdout.write("Ûnicöde")
Ûnicöde7
Run Code Online (Sandbox Code Playgroud)

现在,如果你的Python不知道你的stdouts编码究竟是什么,那就是一个不同的问题,很可能是在Python的构建中.

  • 我的上下文是在Apache下运行Python脚本作为CGI,默认输出编码不是我需要的(我需要UTF-8).我认为脚本最好确保其输出的编码正确,而不是依赖外部设置(如PYTHONIOENCODING等环境变量). (2认同)
  • 虽然 `sys.stdout` 确实是 Python 2 中的*二进制* 文件和 Python 3 中的 *文本* 文件,但我认为您的 Python 2 示例失败了,因为 unicode 字符串 `u"ûnicöde"` 被隐式编码`sys.stdout.write` 方法中的字符超出了 ASCII 范围。如果将“LC_CTYPE”、“LANG”或“PYTHONIOENCODING”环境变量更改为包含 unicode 字符串中所有字符的编码,则不应出现任何错误。(我已经尝试过Python 2.7。) (2认同)