如何解决如何在Powershell中将程序的非ascii输出保存到文件?
我想在 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
Golang
package main
import "fmt"
func main() {
fmt.Printf("Hello ąćęłńóśźż world\n")
}
结果:
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
,
解决方案是启用 Beta: Use Unicode UTF-8 for worldwide language support
,如 What does "Beta: Use Unicode UTF-8 for worldwide language support" actually do?
注意:此解决方案可能会导致旧程序出现问题。请阅读 mklement0 的回答和 Quciksilver 的回答以了解详细信息和替代解决方案。
我还发现 Ghisler 写的解释很有帮助 (source):
如果选中此选项,Windows 将使用代码页 65001 (Unicode UTF-8) 而不是像 1252 (Western Latin1) 这样的本地代码页 所有纯文本文件。优点是在例如创建的文本文件 俄语语言环境也可以在其他语言环境中阅读,例如西方或 欧洲中部。缺点是只有 ANSI 的程序(大多数较旧的 程序)将显示垃圾而不是重音字符。
当启用此选项时,也是 7.1 has a bug 版之前的 Powershell。如果启用它,您可能需要升级到 7.1 或更高版本。
我喜欢这个解决方案,因为设置一次就足够了,而且它正在工作。它为 Windows 带来了一致的类 Unix UTF-8 行为。我希望我不会看到任何问题。
如何启用它:
- Win+R →
intl.cpl
-
Administrative
标签 - 点击
Change system locale
按钮 - 启用
Beta: Use Unicode UTF-8 for worldwide language support
- 重启
或者通过 reg
文件:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]
"ACP"="65001"
"OEMCP"="65001"
"MACCP"="65001"
,
注意:这些字符编码问题仅困扰Windows两个版本中的PowerShell。在类 Unix 平台上,始终使用 UTF-8。[1]
Quicksilver's answer 基本上是正确的:
-
存储在
[Console]::OutputEncoding
中的字符编码决定了 PowerShell 如何解码从外部程序接收的文本[2] - 并注意它总是将这样的输出解释为文本(字符串)。-
[Console]::OutputEncoding
默认反映控制台的活动代码页,它本身默认为系统的活动 OEM 代码页,例如437
({{3} }) 在美国英语系统上。 -
标准的
chcp
程序还报告活动的 OEM 代码页,虽然它原则上也可用于更改活动控制台的代码页(例如,{{ 1}}),由于 .NET 缓存编码,这在 inside PowerShell 中不能工作。
-
-
因此,您可能必须(暂时)设置
chcp 65001
以匹配给定外部控制台程序使用的实际字符编码:-
虽然许多控制台程序尊重活动控制台代码页(在这种情况下不需要变通方法),但有些不,通常是为了提供完整的Unicode 支持。请注意,您可能不会注意到问题,直到您以编程方式处理这样一个程序的输出(意思是:捕获一个变量,通过管道发送到另一个命令,重定向到一个文件),因为当它的标准输出直接连接到控制台时,这样的程序可能会检测到这种情况,然后可以选择性地使用完整的 Unicode 支持进行显示。
-
值得注意的 CLI 不尊重活动控制台代码页:
-
Python 表现出非标准行为,因为它默认使用活动的 ANSI 代码页,即通常仅由非 Unicode 使用的代码页GUI-子系统应用程序。
- 但是,您可以在调用 Python 脚本之前使用
[Console]::OutputEncoding
来指示 Python 使用 UTF-8(然后适用于从同一进程进行的所有 Python 调用);在 v3.7+ 中,您也可以将命令行选项$env:PYTHONUTF8=1
(区分大小写)作为每次调用选择加入。
- 但是,您可以在调用 Python 脚本之前使用
-
Go 和 Node.js 总是使用 UTF-8 编码。
-
-
以下代码段显示了如何根据需要设置 -X utf8
临时:
[Console]::OutputEncoding
CP437 提供了一个有效的替代方案,但它带有警告:
-
通过控制面板(或等效的注册表设置)激活
# 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:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP)) python hello.py | Write-Output # Restore the original encoding. [Console]::OutputEncoding = $orig
功能会更改代码页系统范围,这不仅会影响所有控制台窗口和控制台应用程序,以及传统(非 Unicode)GUI 子系统应用程序,前提是 OEM 和 ANSI 代码页都在设置中。 -
显着的副作用包括:
-
Windows PowerShell 的默认行为发生了变化,因为它使用 ANSI 代码页来读取源代码并作为Your own answer 和
Get-Content
cmdlet 的默认编码。-
例如,包含非 ASCII 范围字符(例如
Use Unicode UTF-8 for worldwide language support
)的现有 Windows PowerShell 脚本将出现错误行为,除非将它们保存为 UTF-8 一个 BOM(或作为“Unicode”,UTF-16LE,它总是有一个 BOM)。 -
相比之下,PowerShell (Core) v6+ 始终使用(无 BOM)UTF-8。
-
-
旧的控制台应用程序可能会中断使用
é
(UTF-8) 作为活动的 OEM 代码页,因为它们可能无法处理可变长度编码方面UTF-8(单个字符最多可编码4个字节)。
-
-
有关详细信息,请参阅
Set-Content
。
[1] 跨平台 this answer 始终使用(无 BOM)UTF-8。虽然可以配置 Unix 终端,从而配置控制台(终端)应用程序以使用 other UTF-8 以外的字符编码,但如今这样做很少见 - UTF-8 几乎普遍使用。上>
[2] 相比之下,PowerShell (Core) v6+ edition 决定了用于通过管道发送文本到外部程序的编码。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。