use*_*ser 6 powershell encoding byte-order-mark pipe
我正在尝试通过以下方式重定向 PowerShell 中的输入:
Get-Content input.txt | my-program args
Run Code Online (Sandbox Code Playgroud)
问题是管道 UTF-8 文本前面有一个 BOM (0xEFBBBF),我的程序无法正确处理它。
一个最小的工作示例:
// File: Hex.java
import java.io.IOException;
public class Hex {
public static void main(String[] dummy) {
int ch;
try {
while ((ch = System.in.read()) != -1) {
System.out.print(String.format("%02X ", ch));
}
} catch (IOException e) {
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在 PowerShell 中:
javac Hex.java
Set-Content textfile "ABC" -Encoding Ascii
# Now the content of textfile is 0x41 42 43 0D 0A
Get-Content textfile | java Hex
Run Code Online (Sandbox Code Playgroud)
或者干脆
javac Hex.java
Write-Output "ABC" | java Hex
Run Code Online (Sandbox Code Playgroud)
在任何一种情况下,输出都是EF BB BF 41 42 43 0D 0A。
如何在没有 0xEFBBBF 的情况下将文本通过管道传输到程序中?
\n
注意:
\n以下内容包含在正常运行的 PowerShell 环境中可以解释 OP 症状的一般信息。该解决方案在OP的情况下不起作用是由于目前未知的机器特定原因。
\n这个答案是关于将 BOM-less UTF-8 发送到外部程序;如果您希望使 PowerShell 控制台窗口在所有方面都使用 UTF-8,请参阅此答案。\n
为了确保您的 Java 程序接收到不带 BOM 的UTF-8 编码输入,您必须设置$OutputEncoding为System.Text.UTF8Encoding不发出BOM 的实例:
# Assigns UTF-8 encoding *without a BOM*.\n# PowerShell uses this encoding to encode data piped to external programs.\n# $OutputEncoding defaults to ASCII(!) in Windows PowerShell, and more sensibly\n# to BOM-*less* UTF-8 in PowerShell [Core] v6+\n$OutputEncoding = [Text.UTF8Encoding]::new($false)\nRun Code Online (Sandbox Code Playgroud)\n注意事项:
\n不要使用看似等效的New-Object Text.Utf8Encoding $false,因为,由于GitHub 问题 #5763$OutpuEncoding中描述的错误,如果您在非全局范围内(例如在脚本中)分配 to ,它将不起作用。在 PowerShell v4 及更低版本中,使用
\n(New-Object Text.Utf8Encoding $false).psobject.BaseObject作为解决方法。
Windows 10 版本 1903 及更高版本允许您将BOM-less UTF-8 设置为系统范围的默认编码(尽管请注意,该功能自版本 20H2 起仍被归类为beta ) - 请参阅此答案;[在 PowerShell 7.1 中已修复]在 PowerShell [Core] 至 v7.0 中,打开此功能后,上述技术无效,因为假定的 .NET Core错误会导致始终发出UTF-8 BOM ,无论您设置为何种编码$OutputEncoding(该错误可能与GitHub 问题 #28929有关);唯一的解决方案是关闭该功能,如imgx64 的答案所示。
相比之下,如果您使用[Text.Encoding]::Utf8,您将获得一个带有 BOM 的System.Text.Encoding.UTF8实例- 这就是我怀疑在您的情况下发生的情况。
请注意,此问题与所读取的任何文件的源编码无关Get-Content,因为通过 PowerShell 管道发送的绝不是原始字节流,而是.NET 对象,在这种情况下Get-Content意味着发送.NET字符串(System.String,内部是 UTF-16 代码单元的序列)。
因为您正在通过管道传输到外部程序(在您的情况下是 Java 应用程序),所以 PowerShell 根据首选项变量$OutputEncoding对发送给它的(按需字符串化)对象进行字符编码,并且生成的编码是外部程序的编码收到。
也许令人惊讶的是,尽管 BOM 通常仅在文件中$OutputEncoding使用,但 PowerShell也尊重分配给管道中的编码的 BOM 设置,将其添加到(仅)发送的第一行。
有关 PowerShell 如何处理外部程序的管道输入和输出的更多信息,请参阅本答案的底部部分,包括PowerShell 解释从外部程序接收到的数据时的重要性[Console]::OutputEncoding。
要使用示例程序说明差异(请注意如何使用 PowerShell 字符串文字作为输入就足够了;无需从文件中读取):
\n# Note the EF BB BF sequence representing the UTF-8 BOM.\n# Enclosure in & { ... } ensures that a local, temporary copy of $OutputEncoding\n# is used.\nPS> & { $OutputEncoding = [Text.Encoding]::Utf8; \'h\xc3\xb6\' | java Hex }\nEF BB BF 68 C3 B6 0D 0A\n\n# Note the absence of EF BB BF, due to using a BOM-less\n# UTF-8 encoding.\nPS> & { $OutputEncoding = [Text.Utf8Encoding]::new($false); \'h\xc3\xb6\' | java Hex }\n68 C3 B6 0D 0A\nRun Code Online (Sandbox Code Playgroud)\n在默认为 ASCII(!) 的Windows PowerShell中$OutputEncoding,您将看到以下内容(默认情况下):
# The default of ASCII(!) results in *lossy* encoding in Windows PowerShell.\nPS> \'h\xc3\xb6\' | java Hex \n68 3F 0D 0A\nRun Code Online (Sandbox Code Playgroud)\n请注意,3F表示文字?字符,这也是非 ASCII\xc3\xb6字符的音译,因为它没有 ASCII 表示形式;换句话说:信息丢失了。
PowerShell [Core] v6+现在明智地默认为无 BOM UTF-8,因此默认行为符合预期。
\n虽然无 BOM UTF-8 是 PowerShell [Core] 的一致默认值,但对于读取和写入文件的 cmdlet 来说也是如此,但在Windows [Console]::OutputEncoding上,默认情况下自 v7.0 起仍反映活动的 OEM 代码页,因此要正确捕获来自发送 UTF-8 的外部程序的输出,它[Text.UTF8Encoding]::new($false)也必须设置为- 请参阅GitHub 问题 #7233。
您可以尝试将 OutputEncoding 设置为 UTF-8 without BOM:
# Keep the current output encoding in a variable
$oldEncoding = [console]::OutputEncoding
# Set the output encoding to use UTF8 without BOM
[console]::OutputEncoding = New-Object System.Text.UTF8Encoding $false
Get-Content input.txt | my-program args
# Reset the output encoding to the previous
[console]::OutputEncoding = $oldEncoding
Run Code Online (Sandbox Code Playgroud)
如果上述方法没有效果,并且您的程序确实理解 UTF-8,但只希望它没有 3 字节 BOM,那么您可以尝试从内容中删除 BOM 并将结果通过管道传递给您的程序
(Get-Content 'input.txt' -Raw -Encoding UTF8) -replace '^\xef\xbb\xbf' | my-program args
Run Code Online (Sandbox Code Playgroud)
如果您使用 '破解' 了代码页chcp 65001,我建议将其转回chcp 5129英语 - 新西兰。看这里。
| 归档时间: |
|
| 查看次数: |
750 次 |
| 最近记录: |