重定向输出时,Windows编码发生了变化。

8

你好,我有一个名为'test.py'的Python文件:

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

当我运行时

py test.py

我得到:

utf-8
utf-8

但是当我运行时

py test.py > test.txt

或者

py test.py | Out-File -FilePath test.txt -Encoding ASCII

我从test.txt获取:

cp1252
utf-8

当我运行时:

import sys, locale
print(sys.getdefaultencoding())
print(locale.getpreferredencoding())

我得到:

utf-8
cp1252

问题:
请问为什么会出现这种情况,以及在重定向时如何使默认编码为 utf-8?
谢谢


2
最简单的解决方案是运行一个使用重定向通过 cmd.exe /c "命令行" 管道或文件的命令。PowerShell与我使用过的每个Shell都不同,标准I/O重定向将Shell设置为中间人,解码文本,转换LF <-> CRLF换行符,然后重新进行编码。我从来不想要这个。默认情况下,Shell应直接连接管道和文件重定向,而不涉及自身。CMD做得很好。 - Eryk Sun
2
顺便提一下,PowerShell控制台并不存在。它与所有其他控制台应用程序(包括python.exe)使用相同的Windows控制台(conhost.exe)。此外,Windows Python默认为locale.getpreferredencoding()用于非控制台I/O,而对于控制台I/O则为UTF-8(在内部转码为控制台的宽字符API所需的UTF-16)。sys.getdefaultencoding()是脚本文件和str.encode的默认编码。 - Eryk Sun
1
我们可以通过PYTHONIOENCODING环境变量来覆盖非控制台sys.std*文件的默认编码。在较新的版本中,我们可以通过命令行选项-Xutf8将默认编码覆盖为UTF-8。 - Eryk Sun
2
为了简单起见,只需运行 cmd /c "py.exe -Xutf8 test.py > test.txt"。要使 -Encoding 生效,PowerShell 必须先将程序的 stdout 解码为 UTF-16,然后重新编码,这可能会产生乱码,除非您手动匹配 PowerShell 的输入文本编码与程序的 stdout 编码相同。如果您对程序本身有控制权,例如使用 Python 脚本,则最好将 PowerShell 排除在外。 - Eryk Sun
1
在PowerShell中,> test.txt不会将命令的stdout设置为指向“test.txt”文件的句柄,而是将其设置为具有PowerShell另一端的管道。这与我使用过的所有其他shell都不同。由程序(例如Python)写入stdout的编码字节会被PowerShell 转码,包括LF -> CRLF转换,然后写入目标文件。在PowerShell内部的解决方法非常复杂。我的意思是通过运行cmd.exe /c "command line"来简单地绕过这种烦人的行为。 - Eryk Sun
显示剩余20条评论
2个回答

2

请问这是为什么?

因为Python开发人员选择这样做。 请参阅文档:

在Windows上,控制台设备使用UTF-8。磁盘文件和管道等非字符设备使用系统区域设置编码(即ANSI代码页)。

如果要重定向时默认编码为UTF-8,我该怎么办?

强制编码。 对于遇到此问题的程序,我已添加了以下内容:

if os.name == "nt":
    sys.stdout.reconfigure(encoding="utf-8")

如果您使用 stderr,您可能也想重新配置它。

1
你正在使用Windows。这是因为Windows 7控制台不理解UTF-8所导致的。因此,当你显示标准输出时,需要将其编码为Windows可以显示的格式。
Luciano Ramalho的书 Fluent Python 做了很好的解释。

2
它确实能够理解UTF-8,只是默认情况下不会。您需要更改代码页:CHCP 65001 - Cody Gray
这个问题与控制台无关。话虽如此,在Windows 10中,控制台仍然存在UTF-8(代码页65001)的错误。例如,chcp.com设置了输入和输出控制台代码页,并将输入代码页设置为UTF-8会将输入限制为7位ASCII码。在Windows 10中,非ASCII字符被读取为null字节("\x00"),而在早期版本的Windows中,整个读取看起来像EOF(即成功读取0字节)。在Windows 7及更早版本中,UTF-8输出也存在错误,但Windows 7已接近生命周期结束。 - Eryk Sun
@CodyGray 嗯,将代码页更改为65001并没有起作用,感谢您的建议。 - Kallzvx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接