问题:
截至本文撰写时,mosquitto_sub
man page没有提到字符编码的问题。但是,在Windows上,mosquitto_sub
表现出了非标准行为,它使用系统活动的ANSI代码页来编码其字符串输出,而不是控制台应用程序预期使用的OEM代码页。[1]
似乎也没有任何选项可以允许您指定要使用的编码方式。
PowerShell将外部应用程序的输出解码为.NET字符串,基于存储在[Console] :: OutputEncoding中的编码,默认为OEM代码页。因此,当它看到字符é的ANSI字节表示形式0xe9在输出中时,它将其解释为OEM表示形式,在其中它表示字符Θ(假设活动ANSI代码页是Windows-1252,活动OEM代码页IBM437,例如在美国英语系统中)。您可以按以下方式进行验证:
PS> $oemEnc = [System.Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage OEMCP));
$oemEnc.GetString([byte[]] 0xe9)
Θ
请注意,解码为.NET字符串(
System.String
)的过程中,字符以UTF-16代码单元的形式存储在内存中,基本上是作为组成.NET字符串的
System.Char
实例的
[uint16]
值。这样的代码单元可以完整地编码一个Unicode字符,或者 - 对于位于所谓BMP(基本多文种平面)之外的字符 - 作为所谓代理对的一部分,编码Unicode字符的一半。
在这种情况下,这意味着
Θ
字符以不同的代码点存储,即Unicode代码点:
Θ
(希腊大写字母theta,
U+0398
)。
解决方案:
注意: 解决问题的一个简单方法是激活 Windows 10 中的全局 UTF-8 支持,这将同时设置 ANSI 和 OEM 代码页为65001
,即 UTF-8。但是,该功能(a)截至本文仍处于测试版状态且(b)具有深远的影响-有关详细信息,请参见this answer。
然而,它实际上是最基本的解决方案,因为它还可以使跨平台使用 Mosquitto 正常工作(在类 Unix 平台上,Mosquitto 使用 UTF-8)。
Powershell 在此情况下必须指定要使用的字符编码,可以按以下方式完成:
PS> $msg = & {
$prevEnc = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP))
mosquitto_sub -h test.mosquitto.org -t tofol/test
[Console]::OutputEncoding = $prevEnc
}; $msg
{ "label": "eé" }
注意:
Get-ItemPropertyValue
cmdlet需要PowerShell 5或更高版本;在早期版本中,可以使用
[Console] :: OutputEncoding = [System.TextEncoding] :: Default
,或者如果代码还必须在PowerShell(Core)中运行,则使用
[Console] :: OutputEncoding = [System.Text.Encoding] :: GetEncoding([int](Get-ItemProperty HKLM:\ SYSTEM \ CurrentControlSet \ Control \ Nls \ CodePage ACP)。ACP)
。
辅助函数Invoke-WithEncoding
可以为您封装此过程。您可以直接安装它从Gist 如下所示(我可以向您保证这样做是安全的,但您始终应该检查):
irm https://gist.github.com/mklement0/ef57aea441ea8bd43387a7d7edfc6c19/raw/Invoke-WithEncoding.ps1 | iex
这个解决方法可以简化为:
PS> Invoke-WithEncoding -Encoding Ansi { mosquitto_sub -h test.mosquitto.org -t tofol/test }
{ "label": "eé" } # OK
一个类似的专注于诊断输出的函数是Debug-NativeInOutput
,在this answer中讨论。
作为旁注:
虽然 PowerShell 不是这里的问题,但它也可能表现出有问题的字符编码行为。
GitHub issue #7233 提议使 PowerShell(核心)窗口默认使用 UTF-8,以最小化与大多数现代命令行程序的编码问题(但对于 mosquitto_sub 无济于事),
this comment 具体阐述了该提议。
[1] 注意,Python也表现出这种非标准行为,但它提供了UTF-8编码作为一种选择,可以通过将环境变量PYTHONUTF8
设置为1
或者通过v3.7+ CLI选项-X utf8
(必须精确指定大小写)来启用。