我有以下两行Python(v.3.10.7)程序“stdin.py”:
import sys
print(sys.stdin.read())
以及以下一行文本文件“ansi.txt”(CP1252编码)包含:
‘I am well’ he said.
请注意,开盘价和收盘价分别为
0x91
和 0x92
。在 Windows-10 cmd 模式下,Python 代码的行为符合预期:
python stdin.py < ansi.txt # --> ‘I am well’ he said.
另一方面,在 Windows Powershell 中:
cat .\ansi.txt | python .\stdin.py # --> ?I am well? he said.
显然 CP1252 字符在 Python/PowerShell 的组合。如果我用文件输入替换“stdin.py”中的标准输入,Python 会正确地将 CP1252 引号字符打印到屏幕上。 PowerShell 本身可以正确识别并打印
0x91
和 0x92
。
问题:有人可以向我解释为什么 cmd 与 PowerShell 与 Python 结合使用时的工作方式不同吗?为什么当 PowerShell 通过管道输入 CP1252 引号字符
0x91
和 0x92
时,Python 无法识别它们?
tl;博士
$OutputEncoding
偏好变量:
# Using the system's legacy ANSI code page, as Python does by default.
# NOTE: The & { ... } enclosure isn't strictly necessary, but
# ensures that the $OutputEncoding change is only temporary,
# by limiting to the child scope that the enclosure cretes.
& {
$OutputEncoding = [System.Text.Encoding]::Default
"‘I am well’ he said." | python -c 'import sys; print(sys.stdin.read())'
}
# Using UTF-8 instead, which is generally preferable.
# Note the `-X utf8` option (Python 3.7+)
& {
$OutputEncoding = [System.Text.UTF8Encoding]::new()
"‘I am well’ he said." | python -X utf8 -c 'import sys; print(sys.stdin.read())'
}
# Using the system's legacy ANSI code page, as Python does by default.
# Note: In PowerShell (Core) / .NET 5+,
# [System.Text.Encoding]::Default` now reports UTF-8,
# not the active ANSI encoding.
& {
$OutputEncoding = [System.Text.Encoding]::GetEncoding([cultureinfo]::CurrentCulture.TextInfo.ANSICodePage)
"‘I am well’ he said." | python -c 'import sys; print(sys.stdin.read())'
}
# Using UTF-8 instead, which is generally preferable.
# Note the `-X utf8` option (Python 3.7+)
# NO need to set $OutputEncoding, as it now *defaults* to UTF-8
"‘I am well’ he said." | python -X utf8 -c 'import sys; print(sys.stdin.read())'
注:
$OutputEncoding
控制使用什么编码通过管道(到标准输入)将数据发送到外部程序。在 Windows PowerShell 中默认为 ASCII(!),在 PowerShell (Core) 中默认为 UTF-8。
437
)。
python
调用接收数据以进行,您也必须(临时)设置
[Console]::OutputEncoding
- 请参阅此答案这两种编码在默认情况下不对齐是不幸的;虽然 Windows PowerShell 不会再有任何变化,但 PowerShell(核心) 还是有希望的:将其默认为 一致
为 UTF-8 是有意义的:建议至少将启动 PowerShell 的快捷方式文件默认为 UTF-8(代码页
65001
); GitHub 问题 #14945 更广泛地讨论有问题的不匹配问题。在 Windows 10 及更高版本中,有一个选项可以切换到 UTF-8 系统范围
,这将使 OEM 和 ANSI 代码页默认为 UTF-8 (
65001
);然而,这会产生深远的影响,并且从 Windows 11 开始仍被标记为处于测试版 - 请参阅这个答案。$OutputEncoding
首选项变量决定了 PowerShell 使用哪种字符编码通过管道将数据(始终为文本,从 PowerShell 7.3 开始)发送到外部程序。请注意,这甚至适用于从文件读取数据时:PowerShell,从 v7.3 开始,从不通过管道发送原始字节:它首先将内容读入 .NET 字符串,然后通过管道将它们发送到外部程序时,根据 $OutputEncoding
对它们进行重新编码。
ansi.txt
输入文件使用什么编码最终是无关紧要的,只要 PowerShell 在将其读入 .NET 字符串(内部由 UTF-16 代码单元组成)时能够正确解码。
请参阅$OutputEncoding
中的字符编码必须与目标程序期望的编码相匹配。
默认情况下$OutputEncoding
中的编码与控制台活动代码页隐含的编码无关
(其本身默认为系统的旧版OEM代码页,例如美式英语系统上的
437
) ,这至少是传统控制台应用程序倾向于使用的;然而,Python 不,并且使用遗留的 ANSI 代码页;其他现代 CLI,尤其是 Node.js 的
node.exe
,始终使用 UTF-8。
虽然PowerShell (Core) 7+ 中
$OutputEncoding
的默认值现在是 UTF-8,但遗憾的是,Windows PowerShell 的默认值是 ASCII(!),这意味着非 ASCII 字符会“丢失”音译为 verbatim ASCII
?
字符,这就是您所看到的。
因此,您必须(暂时)将
$OutputEncoding
设置为 Python 期望的编码和/或要求它使用 UTF-8。