使用变音符号解码 cp1252 标准输出

Decode cp1252 stdout with umlauts

我想列出本地 Windows 机器上的服务 运行。为此,我正在使用此代码:

with Popen(["net", "start"], stdout=PIPE, stderr=PIPE) as p:
    stdout, stderr = p.communicate()  # type: (bytes, bytes)

部分输出如下所示(德语 Windows 版本):

Folgende Windows-Dienste sind gestartet:

   Anmeldedienst
   [...]
   Benachrichtigungsdienst für Systemereignisse

将字节解码为字符串应该很容易。查了下文档,在命令行找到本地编码:

>>> import locale
>>> locale.getpreferredencoding()
'cp1252'

因此,我想解码字节:

lines = stdout.decode('cp1252')

但是,对于示例中列出的变音符号 ü,我收到一条错误消息:

File "test.py", line 511, in my_func
    lines = stdout.decode('cp1252')
File "C:\Python344\lib\encodings\cp1252.py", line 15, in decode
    return codecs.charmap_decode(input,errors,decoding_table)
UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 152: character maps to <undefined>

不过,我知道 UTF-8 contains a representation of the ü character:

U+00FC  ü   c3 bc   LATIN SMALL LETTER U WITH DIAERESIS

我很困惑为什么这个翻译不能完成。

备注:

编辑 1:这里有事情发生。尽管 Python 3.4 文档指出 Windows 上的默认编码是 cp1252,但当 运行 CLI 上的命令并重定向到文件时,文件编码为 IBM850。最好的猜测是 locale.getpreferredencoding() 没有 return CLI 的编码。

编辑 2:使用 ibm850 对标准输出进行解码非常有效。这意味着对 Popen 调用使用 universal_newlines=True 确实使用 return 由 locale.getpreferredencoding() 编辑的编码,但是,这是解码 CLI 输出的错误编码。

解决方法是:

当使用带有 universal_newslines=TruePopen 调用时,会强制对返回的 stdout、stderr 数据进行字符串解释。但是,解释是使用 locale.getpreferredencoding() 返回的编码完成的,在本例中为 cp1252。然而,命令行上的数据不是使用 cp1252(Windows-like ANSI 风格编码)编码,而是使用 ibm850(DOS 编码)。因为解码是对bytes和int进行操作的,并且因为ü(0x81)的stdout中的int在cp1252解码table中没有对应的值,所以[=深处的异常28=] 被抛出。

手动解码命令行数据 str.decode('ibm850') 正确解码。

注意:从 Python 3.5 开始,Popen 允许传递用于数据流的编码,Python 3.4 缺少。