【问题标题】:PowerShell Core not recognising unicodePowerShell Core 无法识别 unicode
【发布时间】:2019-01-19 01:58:14
【问题描述】:

我有一个简单的 PowerShell Core 脚本:

$Message = [IO.File]::ReadAllText("$PSScriptRoot\русский.txt", [System.Text.Encoding]::Default)
$Message

据我所知,PowerShell Core 是 UTF-8默认。但是,正如您在输出中看到的那样,它实际上在 unicode 字符方面比 PowerShell 5.1 更糟糕。

相同的脚本在 PowerShell 5.1 上运行良好

将“ReadAllText”更改为

$Message = [IO.File]::ReadAllText("$PSScriptRoot\русский.txt")

什么都不改变(因为它不应该改变,因为它是读取操作的编码,但只是为了清楚:))。

【问题讨论】:

  • 你能显示[BitConverter]::ToString([IO.File]::ReadAllBytes('E:\asd.ps1'))的输出吗?
  • @PetSerAl, Sure - 24-4D-65-73-73-61-67-65-20-3D-20-5B-49-4F-2E-46-69-6C-65-5D-3A-3A-52-65-61-64-41-6C-6C-54-65-78-74-28-22-24-50-53-53-63-72-69-70-74-52-6F-6F-74-5C-F0-F3-F1-F1-EA-E8-E9-2E-74-78-74-22-2C-20-5B-53-79-73-74-65-6D-2E-54-65-78-74-2E-45-6E-63-6F-64-69-6E-67-5D-3A-3A-44-65-66-61-75-6C-74-29-0D-0A-24-4D-65-73-73-61-67-65 正在寻找 char 信息,我想?
  • 你的脚本文件不是UTF-8编码,而是codepage 1251,因此PowerShell Core(默认为UTF-8)无法读取。

标签: powershell unicode utf-8 powershell-core


【解决方案1】:

文件不包含字符,它们包含字节。要从字节中获取字符,您需要应用一些编码。如果你对相同的字节应用不同的编码,那么你可以在结果中得到不同的字符。

以你的字节串为例:

PS> $ByteArray = [Byte[]]('24-4D-65-73-73-61-67-65-20-3D-20-5B-49-4F-2E-46-69-6C-65-5D-3A-3A-52-65-61-64-41-6C-6C-54-65-78-74-28-22-24-50-53-53-63-72-69-70-74-52-6F-6F-74-5C-F0-F3-F1-F1-EA-E8-E9-2E-74-78-74-22-2C-20-5B-53-79-73-74-65-6D-2E-54-65-78-74-2E-45-6E-63-6F-64-69-6E-67-5D-3A-3A-44-65-66-61-75-6C-74-29-0D-0A-24-4D-65-73-73-61-67-65' -split '-' | % { [Byte]::Parse($_, 'HexNumber') })
PS> [Text.Encoding]::UTF8.GetString($ByteArray)
$Message = [IO.File]::ReadAllText("$PSScriptRoot\�������.txt", [System.Text.Encoding]::Default)
$Message
PS> [Text.Encoding]::GetEncoding(1251).GetString($ByteArray)
$Message = [IO.File]::ReadAllText("$PSScriptRoot\русский.txt", [System.Text.Encoding]::Default)
$Message
PS> [Text.Encoding]::GetEncoding(1252).GetString($ByteArray)
$Message = [IO.File]::ReadAllText("$PSScriptRoot\ðóññêèé.txt", [System.Text.Encoding]::Default)
$Message

读取文件时使用正确的编码很重要。需要注意的一件重要事情是,您的脚本文件使用代码页 1251,而不是 UTF-8。另请注意,根据 UTF-8,字节序列 F0-F3-F1-F1-EA-E8-E9(在代码页 1251 中代表世界 русский)是无效的字节序列,因此您将获得七个替换字符(U+FFFD)。

由于 PowerShell Core 默认使用 UTF-8 并且您的脚本文件没有 BOM 来指示其他情况(尽管没有可以使 PowerShell 识别代码页 1251 的 BOM),PowerShell Core 使用 UTF-8 编码读取您的文件,因此它试图访问�������.txt(你没有)而不是русский.txt

如果您将脚本更改为写入文件而不是读取文件,您可以自己轻松观察。

PS> $ByteArray2 = [Byte[]](91, 73, 79, 46, 70, 105, 108, 101, 93, 58, 58, 87, 114, 105, 116, 101, 65, 108, 108, 84, 101, 120, 116, 40, 34, 36, 80, 83, 83, 99, 114, 105, 112, 116, 82, 111, 111, 116, 92, 240, 243, 241, 241, 234, 232, 233, 46, 116, 120, 116, 34, 44, 32, 91, 68, 97, 116, 101, 84, 105, 109, 101, 93, 58, 58, 85, 116, 99, 78, 111, 119, 41)
PS> # Representing `[IO.File]::WriteAllText("$PSScriptRoot\русский.txt", [DateTime]::UtcNow)` in codepage 1251
PS> [IO.File]::WriteAllBytes("$(Convert-Path .)\write.ps1", $ByteArray2)
PS> .\write.ps1

现在您可以使用原始脚本读回文件:

PS> [IO.File]::WriteAllBytes("$(Convert-Path .)\asd.ps1", $ByteArray)
PS> .\asd.ps1
01/18/2019 17:13:15

使用 PowerShell Core 调用这两个脚本:

PS> pwsh -Command ".\write.ps1; .\asd.ps1"
01/18/2019 17:21:02

如您所见,您的脚本已在 PowerShell Core 中成功执行。如果您浏览当前目录,那么您可以看到,它同时包含русский.txt�������.txt,并且它们的内容匹配,即控制台上打印的内容。

实际上这个问题与读/写文件有关(脚本文件本身除外)。可以用简单的脚本来演示,只打印字符串文字的字符代码:

PS> $ByteArray3 = [Byte[]](40, 39, 240, 243, 241, 241, 234, 232, 233, 39, 46, 71, 101, 116, 69, 110, 117, 109, 101, 114, 97, 116, 111, 114, 40, 41, 32, 124, 32, 37, 32, 84, 111, 73, 110, 116, 51, 50, 32, 36, 110, 117, 108, 108, 32, 124, 32, 37, 32, 84, 111, 83, 116, 114, 105, 110, 103, 32, 88, 52, 41, 32, 45, 106, 111, 105, 110, 32, 39, 45, 39)
PS> # Representing `('русский'.GetEnumerator() | % ToInt32 $null | % ToString X4) -join '-'` in codepage 1251
PS> [IO.File]::WriteAllBytes("$(Convert-Path .)\test.ps1", $ByteArray3)

在 Windows PowerShell 中调用它会产生一个结果:

PS> .\test.ps1
0440-0443-0441-0441-043A-0438-0439

而 PowerShell Core 会产生不同的:

PS> pwsh -Command ".\test.ps1"
FFFD-FFFD-FFFD-FFFD-FFFD-FFFD-FFFD

解决此问题的一种方法是将 UTF-8 与 BOM 结合使用,以确保 Windows PowerShell 和 PowerShell Core 在读取脚本文件时使用相同的编码。

答案是假设 [Text.Encoding]::Default.CodePage 返回 1251,就像 OP 的情况一样。

【讨论】:

    【解决方案2】:

    Windows 使用称为 cp1252 的特定于 Windows 的字符编码。 要使用 Unicode 字符,您需要在第一次使用该文件之前运行此命令:

    chcp 65001 | Out-Null  # set codepage to UTF-8
    $Message = [System.IO.File]::ReadAllText("$PSScriptRoot\русский.txt")
    

    chcp 65001 | Out-Null  # set codepage to UTF-8
    $Message = Get-Content "$PSScriptRoot\русский.txt"
    

    希望有帮助

    【讨论】:

    • 不准确。 Windows 特定字符编码 称为ANSI xxxx 其中xxxx 来自(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage").ACP 例如,有ANSI 1252(美国和西欧)、ANSI 1250(中欧/斯拉夫)、ANSI 1253(希腊语)、ANSI 1251(西里尔文)等(并且没有为 CJK 语言定义)。 CPxxx 是所谓的“OEM”代码页(用于cmd),xxx 来自(Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage").OEMCP(分别为:CP437/850、CP852、CP737/869、CP866)。
    猜你喜欢
    • 2014-07-08
    • 2014-01-08
    • 2012-09-13
    • 2020-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多