提问人:cubanpit 提问时间:11/10/2023 最后编辑:cubanpit 更新时间:11/10/2023 访问量:78
如何让 UTF-8 在 Windows 上的新式 PowerShell 中完美运行?
How do I get UTF-8 to work flawlessly in modern PowerShell on Windows?
问:
我有一个 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
答:
在 Windows 上,有两部分拼图:
必须指示 PowerShell 在与外部程序通信时使用 UTF-8。
使用以下魔法咒语(请注意,这是你要做的,不是一个选项,因为 .NET 缓存了存储在 中的编码):
chcp 65001
cmd.exe
[Console]
$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding
有关背景信息,请参阅此答案。
您还必须指示 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 模式,这将成为 Python 3.15 中的默认模式。
通过一次性配置步骤的另一种方法是将计算机切换为在系统范围内使用 UTF-8,在这种情况下,不需要上述步骤;但是,这会产生深远的影响,并且可能会破坏遗留的脚本和应用程序 - 请参阅此答案。
背景资料:
PowerShell 在一定程度上是一个很好的 Windows 控制台公民:
它使用控制台窗口的活动代码页隐含的编码(一个用于输入,一个用于输出),默认为系统的旧版 OEM 代码页;具体说来:
解码外部程序的输出时,它使用控制台的输出代码页,如 中的 .NET 中所示,这是外部程序在对其输出进行编码时至少在历史上应使用的内容。
[Console]::OutputEncoding
但是,当对输入进行编码以通过管道(目标程序的 stdin 流)提供给外部程序时,PowerShell 会做出一个奇怪的选择;它不使用控制台的活动(输入)代码页,而是使用存储在
$OutputEncoding
首选项变量中的编码,该变量具有意外的默认值:在 Windows PowerShell(旧版、仅限 Windows、随 Windows 一起提供,其最新版本和最新版本为 v5.1)中,它默认为 ASCII(!)
在 PowerShell (Core) 7+(新式跨平台按需安装版本)中,它默认为 UTF-8(!)。
注意:PowerShell 7+ 在读取文件(包括源代码)和写入文件时,内部始终使用(无 BOM)UTF-8,但必须对外部程序的输出进行解码,但必须基于控制台的(输出)代码页。
GitHub 问题 #7233 建议通过将控制台代码页设置为 .
65001
Python 表现出非标准行为:
当它发现其 stdout 流被重定向时,默认情况下,它使用系统的旧版 ANSI(!) 代码页对其输出进行编码。
直接打印到控制台时,捕获或重定向输出时因误解而导致的问题不会浮出水面,因为 Python 随后使用相关的 Unicode WinAPI 打印到控制台,绕过任何编码问题:
- 换句话说:Python 的输出在直接输出时始终正确显示,但在将输出重定向到文件、通过 PowerShell 的管道传递输出或在 PowerShell 变量中捕获输出时,可能会发生误解。
评论
$OutputEncoding
[Console]::OutputEncoding
[Console]::InputEncoding
python -X utf8
评论
chcp 65001
chcp 1252
[Console]