我想在Powershell中运行程序,并用UTF-8编码将输出写入文件。
但是我不能正确地写非ascii字符。
我已经读过很多关于堆栈溢出的类似问题,但我仍然找不到答案。
我尝试了PowerShell 5.1.19041.1023
和PowerShell Core 7.1.3
,它们对输出文件进行了不同的编码,但内容以相同的方式被破坏。
我尝试了Python和Golang中的简单程序:
(请假设我无法更改程序的源代码)
Python
print('Hello ąćęłńóśźż world')
结果:
python hello.py
Hello ąćęłńóśźż world
python hello.py > file1.txt
Hello ╣Šŕ│˝ˇťč┐ world
python hello.py | out-file -encoding utf8 file2.ext
Hello ╣Šŕ│˝ˇťč┐ world
关于cmd
:
python hello.py > file3.txt
Hello ���� world
果朗
package main
import "fmt"
func main() {
fmt.Printf("Hello ąćęłńóśźż worldn")
}
结果:
go run hello.go
:
Hello ąćęłńóśźż world
go run hello.go > file4.txt
Hello ─ů─ç─Ö┼é┼ä├│┼Ť┼║┼╝ world
go run hello.go | out-file -encoding utf8 file5.txt
Hello ─ů─ç─Ö┼é┼ä├│┼Ť┼║┼╝ world
在cmd
上工作正常:
go run hello.go > file6.txt
Hello ąćęłńóśźż world
您应该首先设置控制台的OutputEncoding属性。
在PowerShell中,在运行程序之前输入以下行:
[Console]::OutputEncoding = [Text.Encoding]::Utf8
然后,您可以将Out-File
与您的编码类型一起使用:
py hello.py | Out-File -Encoding UTF8 file2.ext
go run hello.go | Out-File -Encoding UTF8 file5.txt
注意:在这两个版本中,这些字符编码问题仅困扰Windows上的PowerShell。在类Unix平台上,UTF-8一直在使用[1]
Quicksilver的答案基本正确:
-
是存储在
[Console]::OutputEncoding
中的字符编码决定PowerShell如何解码从外部程序 [2]-并注意它总是将此类输出解释为文本(字符串)。-
[Console]::OutputEncoding
默认情况下反映控制台的活动代码页,其本身默认为系统的活动OEM代码页,例如美国英语系统上的437
(CP437)。 -
标准
chcp
程序还报告活动OEM代码页,虽然原则上它也可以用于更改活动控制台的代码页(例如chcp 65001
),但这不会在PowerShell内部的中工作。NET缓存编码。
-
-
因此,您可能需要(临时)设置
[Console]::OutputEncoding
以匹配给定外部控制台程序使用的实际字符编码:-
虽然许多控制台程序尊重活动控制台代码页(在这种情况下不需要解决方案),但有些程序不尊重,通常是为了提供完全的Unicode支持。请注意,您可能不会注意到问题,直到以编程方式处理此类程序的输出(意思是:捕获变量,通过管道发送到另一个命令,重定向到文件),因为这样的程序可以在其stdout直接连接到控制台时检测到这种情况,然后可以选择性地使用完整的Unicode支持来显示。
-
值得注意的不的CLI尊重活动控制台代码页:
-
Python表现出非标准行为,因为它默认使用活动的ANSI代码页,即通常仅由非UnicodeGUI子系统应用程序使用的代码页。
- 但是,在调用Python脚本之前,您可以使用
$env:PYTHONUTF8=1
来指示Python使用UTF-8(这将适用于从同一进程进行的所有Python调用);在v3.7+中,您也可以将命令行选项-X utf8
(区分大小写)作为每次调用的opt-in传递
- 但是,在调用Python脚本之前,您可以使用
-
Go和Node.js都使用UTF-8编码。
-
-
以下片段显示如何根据需要临时设置[Console]::OutputEncoding
:
# Save the original encoding.
$orig = [Console]::OutputEncoding
# Work with console programs that use UTF-8 encoding,
# such as Go and Node.js
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
# Piping to Write-Output is a dummy operation that forces
# decoding of the external program's output, so that encoding problems would show.
go run hello.go | Write-Output
# Work with console programs that use ANSI encoding, such as Python.
# As noted, the alternative is to configure Python to use UTF-8.
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:SYSTEMCurrentControlSetControlNlsCodePage ACP))
python hello.py | Write-Output
# Restore the original encoding.
[Console]::OutputEncoding = $orig
您自己的答案提供了一个有效的替代方案,但它附带了注意事项:
通过控制面板(或等效注册表设置)激活
Use Unicode UTF-8 for worldwide language support
功能会更改整个系统的代码页,这不仅会影响所有控制台窗口和控制台应用程序,还会影响遗留(非Unicode)GUI子系统应用程序,前提是OEM和ANSI代码页都在设置中。显著的副作用包括:
Windows PowerShell的默认行为发生了更改,因为它使用ANSI代码页来读取源代码,并将其作为
Get-Content
和Set-Content
cmdlet的默认编码。例如,包含非ASCII范围字符(如
é
)的现有Windows PowerShell脚本将行为不端,除非它们被保存为UTF-8并带有BOM(或"Unicode"、UTF-16LE,它总是有BOM)。相比之下,PowerShell(Core)v6+一开始就一贯使用(无BOM)UTF-8。
旧的控制台应用程序可能会将
65001
(UTF-8)作为活动OEM代码页来中断,因为它们可能无法处理UTF-8的可变长度编码方面(单个字符最多可编码4个字节)。
有关详细信息,请参阅此答案。
[1]跨平台PowerShell(Core)v6+版本始终使用(无BOM)UTF-8。虽然可以配置Unix终端,从而控制台(终端)应用程序使用UTF-8以外的字符编码,但现在很少这样做——UTF-8几乎被普遍使用
[2]相反,是$OutputEncoding
首选变量决定了用于通过管道向外部程序发送文本的编码
解决方案是启用Beta: Use Unicode UTF-8 for worldwide language support
,如What does"Beta:使用Unicode UTF-8支持全球语言";真的是这样吗?
注意:此解决方案可能会导致遗留程序出现问题。有关详细信息和替代解决方案,请阅读mklement0的回答和 Quciksilver此外,我发现Ghisler写的解释很有帮助(来源): 如果选中此选项,Windows将使用代码页65001(UnicodeUTF-8),而不是像1252(西语Latin1)这样的本地代码页所有纯文本文件。优点是在例如。俄语语言环境也可以在其他语言环境中读取,如西方或中欧。缺点是仅ANSI程序(大多数较旧程序)将显示垃圾而不是重音字符。 当启用此选项时,7.1版之前的Powershell也会出现错误。如果启用它,则可能需要升级到7.1或更高版本。 我喜欢这个解决方案,因为只设置一次就足够了,而且它很有效。它为Windows带来了一致的类Unix UTF-8行为。我希望我不会看到任何问题。 如何启用: 或通过intl.cpl
Administrative
选项卡Change system locale
按钮Beta: Use Unicode UTF-8 for worldwide language support
reg
文件:Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlNlsCodePage]
"ACP"="65001"
"OEMCP"="65001"
"MACCP"="65001"