Windows PowerShell 和 CMD 下的 Python 标准 IO

问题描述 投票:0回答:1

我有以下两行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 无法识别它们?

python powershell input cmd standards
1个回答
3
投票

tl;博士

使用

$OutputEncoding
偏好变量:

  • Windows PowerShell中:
# 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。

  • [Console]::OutputEncoding

    控制如何解码从外部程序(通过标准输出)接收的数据。它默认为控制台的活动代码页,而控制台的活动代码页又默认为系统的旧版 OEM 代码页,例如美式英语系统上的 437
    )。

      请注意,这意味着
    • 如果您想从python调用
      接收数据以进行
      ,您
      也必须(临时)设置
      [Console]::OutputEncoding
      - 请参阅此答案

这两种编码在默认情况下对齐是不幸的;虽然 Windows PowerShell 不会再有任何变化,但 PowerShell(核心) 还是有希望的:将其默认为 一致

为 UTF-8 是有意义的:
  • GitHub 问题 #7233

     建议至少将启动 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。

© www.soinside.com 2019 - 2024. All rights reserved.