【问题标题】:On Windows, PowerShell misinterprets non-ASCII characters in mosquitto_sub output在 Windows 上,PowerShell 会误解 mosquitto_sub 输出中的非 ASCII 字符
【发布时间】:2021-04-07 16:46:14
【问题描述】:

注意:这个自我回答的问题描述了一个特定于在 Windows 上使用 Eclipse Mosquitto 的问题,它会影响 Windows PowerShell 和跨平台 @但是,987654322@ 版。

我使用类似下面的mosquitto_pub 命令来发布一条消息:

mosquitto_pub -h test.mosquitto.org -t tofol/test -m '{ \"label\": \"eé\" }'

注意:" 字符的额外 \-转义,在 Powershell 7.1 中仍然需要,不应该是必需的,但这是一个单独的问题 - 请参阅this answer.

接收该消息通过mosquitto_sub 意外地破坏了非ASCII 字符é 并打印Θ

PS> $msg = mosquitto_sub -h test.mosquitto.org -t tofol/test; $msg

{ "label": "eΘ" }  # !! Note the 'Θ' instead of 'é'
  • 为什么会这样?
  • 我该如何解决这个问题?

【问题讨论】:

    标签: windows powershell character-encoding mosquitto powershell-core


    【解决方案1】:

    问题

    虽然mosquitto_sub man page 在撰写本文时没有提及字符编码,但似乎在Windows 上 mosquitto_sub 表现出非标准行为 因为它使用系统的活动 ANSI 代码页 来编码其字符串输出,而不是控制台应用程序预期使用的 OEM 代码页.[1]

    似乎也没有选项可以让您指定使用什么编码。

    PowerShell 根据 [Console]::OutputEncoding 中存储的编码将外部应用程序的输出解码为 .NET 字符串,该编码默认为 OEM 代码页。因此,当它在输出中看到字符 é0xe9 的 ANSI 字节表示时,它会将其解释为 OEM 表示,其中它表示字符 Θ(假设活动的 ANSI 代码页是 Windows -1252 和活动的 OEM 代码页 IBM437,例如在美国英语系统中的情况)。

    您可以如下验证:

    # 0xe9 is "é" in the (Windows-1252) ANSI code page, and coincides with *Unicode* code point
    # U+00E9; in the (IBM437) OEM code page, 0xe9 represents "Θ".
    PS> $oemEnc = [System.Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage OEMCP)); 
        $oemEnc.GetString([byte[]] 0xe9)
    
    Θ   # Greek capital letter theta
    

    请注意,总是会解码为 .NET 字符串 (System.String),这意味着字符以 UTF-16 代码单元 的形式存储在内存中,本质上是[uint16] 构成 .NET 字符串的 System.Char 实例的基础值。这样的代码单元对 Unicode 字符 进行完整编码,或者 - 对于字符 所谓的 BMP(基本多语言平面)之外的字符 - half Unicode 字符,作为所谓的代理对的一部分。

    在本例中,这意味着Θ 字符被存储为一个不同的 代码点,即一个Unicode 代码点:Θ希腊大写字母thetaU+0398)。


    解决方案

    注意:解决此问题的一种简单方法是激活对 UTF-8 的系统范围支持(在 Windows 10 中可用),这两者 ANSI 和 OEM 代码页为 65001,即 UTF-8。但是,此功能 (a) 在撰写本文时仍处于 beta 阶段,并且 (b) 具有深远的影响 - 详情请参阅 this answer
    然而,它相当于最基本的解决方案,因为它也使 Mosquitto 使用跨平台正常工作(在类 Unix 平台上,Mosquitto 使用 UTF-8)。

    必须指示PowerShell在这种情况下使用什么字符编码,可以按如下方式完成:

    PS> $msg = & { 
          # Save the original console output encoding...
          $prevEnc = [Console]::OutputEncoding
          # ... and (temporarily) set it to the active ANSI code page.
          # Note: In *Windows PowerShell* - only - [System.TextEncoding]::Default work as the RHS too.
          [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding([int] (Get-ItemPropertyValue HKLM:\SYSTEM\CurrentControlSet\Control\Nls\CodePage ACP))
    
          # Now PowerShell will decode mosquitto_sub's output  correctly.
          mosquitto_sub -h test.mosquitto.org -t tofol/test
    
          # Restore the original encoding.
          [Console]::OutputEncoding = $prevEnc
        }; $msg
    
    { "label": "eé" }  # OK
    

    注意: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可以为你封装这个过程。您可以直接安装它from a Gist,如下所示(我可以向您保证,这样做是安全的,但您应该经常检查):

    # Download and define advanced function Invoke-WithEncoding in the current session.
    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 也表现出这种非标准行为,但它通过将环境变量 PYTHONUTF8 设置为 1 或通过 v3.7 提供 UTF-8 编码作为选择加入+ CLI 选项-X utf8(必须指定大小写!)。

    【讨论】:

      猜你喜欢
      • 2016-02-29
      • 1970-01-01
      • 2011-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-22
      相关资源
      最近更新 更多