如何让UTF-8在Windows上的现代PowerShell中正常工作?

cfh9epnr  于 2023-11-21  发布在  Windows
关注(0)|答案(1)|浏览(175)

我有一个C++程序,它输出原始的UTF-8,在Linux上可以正常工作,但是在Windows shell上输出就不那么好了。例如,“®”变成了“«",“©”变成了“”。代码中还有一个Python部分,当打印到shell时,它似乎工作得更好,所以我试着测试一下Python输出。

PS C:\Users\user> python -c 'print("\N{GREEK CAPITAL LETTER DELTA}")' > test_file_python.txt
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\user\AppData\Local\Programs\Python\Python310\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u0394' in position 0: character maps to <undefined>
PS C:\Users\user> python -X utf8 -c 'print("\N{GREEK CAPITAL LETTER DELTA}")' > test_file_python.txt
PS C:\Users\user> cat test_file_python.txt
Δ
PS C:\Users\user> python -c 'print("\N{GREEK CAPITAL LETTER DELTA}")'
Δ
PS C:\Users\user> cat .\test_file_python_wsl.txt  # Generated in WSL with the above commands
Δ
PS C:\Users\user> Format-Hex .\test_file_python.txt

   Label: C:\Users\user\test_file_python.txt

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 E2 95 AC C3 B6 0D 0A                            �ö��

PS C:\Users\user> Format-Hex .\test_file_python_wsl.txt

   Label: C:\Users\user\test_file_python_wsl.txt

          Offset Bytes                                           Ascii
                 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
          ------ ----------------------------------------------- -----
0000000000000000 CE 94 0A                                        ��

字符串
我不明白PowerShell是如何处理编码的,Python在写shell的时候是如何做到这一点的,而在重定向的时候却不行,为什么在WSL中的Linux Bash中完美工作的东西在新的跨平台PowerShell Core中会出现这种问题,而后者应该“正常工作”。
编辑:我忘了添加一些重要信息,我使用PowerShell Core v7.3.6和此编码设置:

PS C:\Users\user> $OutputEncoding

Preamble          :
BodyName          : utf-8
EncodingName      : Unicode (UTF-8)
HeaderName        : utf-8
WebName           : utf-8
WindowsCodePage   : 1200
IsBrowserDisplay  : True
IsBrowserSave     : True
IsMailNewsDisplay : True
IsMailNewsSave    : True
IsSingleByte      : False
EncoderFallback   : System.Text.EncoderReplacementFallback
DecoderFallback   : System.Text.DecoderReplacementFallback
IsReadOnly        : True
CodePage          : 65001

zphenhs4

zphenhs41#

在Windows上,这个难题有两个部分:

  • 您必须指示 PowerShell 在与外部程序通信时使用UTF-8
  • 使用下面的魔法咒语(注意,chcp 65001,这是你从cmd.exe所做的,是 * 不是 * 一个选项,因为.NET * 缓存 * 存储在[Console]中的编码):
$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding

字符串

  • 有关背景信息,请参见this answer
  • 您必须指示 Python 也使用UTF-8 I/O(假设Python v3.7+):
  • -X utf8(大小写)传递给python可执行文件:
python -X utf8 -c 'print("\N{GREEK CAPITAL LETTER DELTA}")' > test_file_python.txt

  • 或者:在调用Python之前,运行$env:PYTHONUTF8=1
  • 上面启用了Python UTF-8 Mode将成为Python 3.15中的默认设置。

通过一次性配置步骤的替代方法将您的计算机切换为使用UTF-8 * 系统范围*,在这种情况下,上述步骤是不必要的;但是,*这具有 * 深远的影响 ,可能会破坏遗留脚本和应用程序-请参阅this answer

背景信息:

  • PowerShell* 是 * 部分 * 好的Windows控制台公民:**
  • 它使用控制台窗口的活动代码页(一个用于输入,一个用于输出)所隐含的编码,默认为系统的旧版OEM代码页;具体而言:
  • 当 * 解码来自外部程序的输出 * 时,它使用控制台的输出代码页,如[Console]::OutputEncoding中的.NET所反映的那样,这是外部程序在编码其输出时至少在历史上预期使用的。
  • 当 * 编码输入 * 通过管道(目标程序的stdin流)提供给外部程序时,PowerShell做出了一个奇怪的选择;而不是使用控制台的活动(输入)代码页,它使用存储在$OutputEncoding首选项变量中的编码,该变量具有意外的默认值:
  • Windows PowerShell(旧版,仅限Windows,随Windows提供的版本,其最新版本为v5.1)中,它默认为ASCII(!)
  • PowerShell (Core) 7+(现代的、跨平台的、按需安装的版本)中,它默认为UTF-8(!)。
  • 注意事项:PowerShell 7+ * 在 * 阅读文件 (包括源代码)和 * 写入文件 * 时, 内部 * 始终使用(无BOM)UTF-8,但是-必须-解码来自 * 外部程序 * 的输出仍然必须基于控制台的(输出)代码页。
  • GitHub issue #7233建议通过将控制台代码页设置为65001,至少使交互式PowerShell会话也默认为UTF-8(针对 * 外部程序 *)。
  • Python* 表现出 * 非标准 * 行为:**
  • 当它发现它的stdout流 redirected 时,默认情况下它使用系统的遗留 ANSI(!)代码页对其输出进行编码。
  • 当 * 直接打印到控制台 * 时,由于捕获或重定向输出时的误解而导致的问题不会 * 出现,因为Python然后使用相关的 Unicode WinAPI打印到控制台,绕过任何编码问题:
  • 换句话说:Python的输出在 * 直接 * 输出时总是 * 正确显示 *,但是当 * 将输出重定向到文件 *,通过 *PowerShell的管道 * 传递它,或者在 *PowerShell变量 * 中捕获它时,可能会发生误解。

相关问题