本文来自依云's Blog,转载请注明。
有时候进程的运行环境里,locale 会被设置成只支持 ASCII 字符集的(比如 LANG=C)。这时候 Python 就会把标准输出和标准错误的编码给设置成 ascii,造成输出中文时报错。
一种解决办法是设置支持 UTF-8 的 locale,但是那需要在 Python 进程启动前设置。启动之后,初始化过了,再设置 locale 也不会重新初始化那些对象。
另一种办法是往 sys.stdout.buffer 这种地方直接写 bytes。理论上完全没问题,但是写起程序来好累……
我就去找了一下怎么优雅地弄一个新的 sys.stdout 出来。Python 3 的 I/O 不再使用 C 标准库的 I/O 函数,而是直接使用 OS 提供的接口。封装位于 io 这个模块里边,有带缓冲的,不带缓冲的,二进制的,文本的。
研究了一下文档可知,sys.stdout 是个 io.TextIOWrapper,有个 buffer 属性,里边是个 io.BufferedWriter。我们用它造一个新的 io.TextIOWrapper,指定编码为 UTF-8:
import sys import io def setup_io(): sys.stdout = sys.__stdout__ = io.TextIOWrapper( sys.stdout.detach(), encoding='utf-8', line_buffering=True) sys.stderr = sys.__stderr__ = io.TextIOWrapper( sys.stderr.detach(), encoding='utf-8', line_buffering=True)
这里除了可以设置编码之外,也可以设置错误处理和缓冲。所以这个技巧也可以用来容忍编码错误、改变标准输出的缓冲(不需要在启动的时候加 -u
了)。
其实这样子还是不够彻底。Python 在很多地方都有用到默认编码。比如 subprocess,指定 universal_newlines=True 时 Python 会自动给标准输入、输出、错误编解码,但是呢,在 Python 3.6 之前,这里的编码是不能手动指定的。还有参数的编码,也是不能指定的(不过可以传 bytes 过去)。
所以,还是想办法去设置合适的 locale 更靠谱……
Mar 06, 2018 11:32:07 AM
这篇文章似乎能启发我的一个问题,但是好像还是没有解决
仙子有兴趣调查一下么
https://stackoverflow.com/questions/48960633/how-to-properly-print-wide-chars-in-cmd-exe-under-chcp-65001-using-python
Mar 06, 2018 11:43:25 AM
在那个下评论了。
话说你 chcp 65001 干嘛。要好好显示文字就用 PyQt 吧。
Mar 06, 2018 12:43:57 PM
调试用的,源目数据都是utf8,没必要适配其他locale,也没必要引入qt这么heavy的组件
看来升级Win10就行了,过段时间就升级了。
win下也可以用个 warkaround,输出到系统调试端口
import ctypes
# output "logging" messages to DbgView via OutputDebugString (Windows only!)
OutputDebugString = ctypes.windll.kernel32.OutputDebugStringW
class DbgViewHandler(logging.Handler):
def emit(self, record):
OutputDebugString(self.format(record))