Powershell Unicode 字符 - Em Dash

Powershell Unicode Characters - Em Dash

我有一个 powershell 脚本,它使用以下命令从 API 中提取数据

Invoke-RestMethod -Method Post -Uri $WebServiceURL -Body $json -ContentType "Application/json" 

API 服务器端的数据包含一个 Em Dash“–”。

当我使用 Postman 提取数据时,它会按原样显示 Em Dash,但是当我使用 Powershell 提取数据并打印输出时,它会显示一些奇怪的字符,如下所示。

OUPath=ABCD.COM/Test/All Users/India/Test/TestâOU/Desktop Users

Em Dash 打印为“â”。

我尝试使用以下命令转换 Powershell 的输出编码,但没有成功。

[Console]::OutputEncoding = [Text.Encoding]::Utf8

当前 Powershell 版本详细信息。

PS Codes> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.1
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.1
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

默认输出编码如下:

PS Codes> [Console]::OutputEncoding


IsSingleByte      : True
BodyName          : IBM437
EncodingName      : OEM United States
HeaderName        : IBM437
WebName           : IBM437
WindowsCodePage   : 1252
IsBrowserDisplay  : False
IsBrowserSave     : False
IsMailNewsDisplay : False
IsMailNewsSave    : False
EncoderFallback   : System.Text.InternalEncoderBestFitFallback
DecoderFallback   : System.Text.InternalDecoderBestFitFallback
IsReadOnly        : True
CodePage          : 437

我过去在这里回答过几个类似的问题(参见 https://whosebug.com/a/58542493/3156906 and https://whosebug.com/a/66404671/3156906),所以我 认为 这个问题是几个因素的组合: :

  • 服务器正在发送一个编码为 utf-8 的响应,但是 没有Content-Encoding header
  • 在没有字符集的情况下,PowerShell 遵循 HTTP 规范并解码为 ISO-8859-1,最终会得到一个您正在逐字写入控制台的损坏的字符串
  • Postman 可能检测到不知何故响应是 utf-8,即使没有字符集,并且正在解码响应流

当然,如果一个字符集参数,那么这个答案的其余部分都是废话!

无论如何,这里有一段简单的脚本可以重现该问题:

# server encodes response text using utf8
PS> $text = "`u{2014}"; # em dash
PS> $bytes = [System.Text.Encoding]::UTF8.GetBytes($text);
PS> write-host $bytes;
226 128 148

# client (Invoke-RestMethod) decodes bytes as ISO-8859-1
PS> $text = [System.Text.Encoding]::GetEncoding("ISO-8859-1").GetString($bytes);
PS> write-host $text;
â

不幸的是,在您的情况下,处理是不可逆的,因为正如 @JosefZ 在评论中指出的那样,一些编码字节在以下情况下被“扼杀”(即丢弃)字节流被解码。

我能真正建议的是:

  • 修复 API(如果您有访问权限),使其发送一个“charset=utf-8”参数,或者,
  • 也许 hard-code 一些特殊处理可以在进行下游处理之前修复已知的错误名称
  • 或者,使用 Invoke-RestMethod-OutFile 参数将响应字节写入文件而不对其进行解码,然后将其作为 utf-8 编码文件读回。

顺便说一句,这是我以前用来检测 encoding/decoding 对导致给定的修改的脚本 - 我每次都是从头开始写的,所以我也可以 post这次在这里,所以我可以稍后再找到它:-)。


$original = "`u{2014}"; # em dash
$mangled  = "`u{00E2}"; # circumflex a

$encodings = [System.Text.Encoding]::GetEncodings() | sort-object -Property "Name";
foreach( $source in $encodings )
{
    foreach( $target in $encodings )
    {
        $bytes = [System.Text.Encoding]::GetEncoding($source.Name).GetBytes($original);
        $text  = [System.Text.Encoding]::GetEncoding($target.Name).GetString($bytes);
        if( $text -eq $mangled )
        {
            write-host "original string = '$original'";
            write-host "mangled string  = '$mangled'";
            write-host "    source encoding = '$($source.Name)'";
            write-host "    target encoding = '$($target.Name)'";
        }
    }
}