使用变音符号解码 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
我很困惑为什么这个翻译不能完成。
备注:
- 我知道在
Popen
调用中使用 universal_newslines=True
时,我可以规避整个考验,但使用它是让我分析编码问题的原因。
- 从 Python 3.5 开始,似乎
Popen
构造函数添加了一个 encoding=
选项,也许这样可以防止出现这样的问题。遗憾的是,Python 3.4 是某些 LTS Linux 版本中包含的版本。
编辑 1:这里有事情发生。尽管 Python 3.4 文档指出 Windows 上的默认编码是 cp1252,但当 运行 CLI 上的命令并重定向到文件时,文件编码为 IBM850。最好的猜测是 locale.getpreferredencoding()
没有 return CLI 的编码。
编辑 2:使用 ibm850
对标准输出进行解码非常有效。这意味着对 Popen
调用使用 universal_newlines=True
确实使用 return 由 locale.getpreferredencoding()
编辑的编码,但是,这是解码 CLI 输出的错误编码。
解决方法是:
当使用带有 universal_newslines=True
的 Popen
调用时,会强制对返回的 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 缺少。
我想列出本地 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
我很困惑为什么这个翻译不能完成。
备注:
- 我知道在
Popen
调用中使用universal_newslines=True
时,我可以规避整个考验,但使用它是让我分析编码问题的原因。 - 从 Python 3.5 开始,似乎
Popen
构造函数添加了一个encoding=
选项,也许这样可以防止出现这样的问题。遗憾的是,Python 3.4 是某些 LTS Linux 版本中包含的版本。
编辑 1:这里有事情发生。尽管 Python 3.4 文档指出 Windows 上的默认编码是 cp1252,但当 运行 CLI 上的命令并重定向到文件时,文件编码为 IBM850。最好的猜测是 locale.getpreferredencoding()
没有 return CLI 的编码。
编辑 2:使用 ibm850
对标准输出进行解码非常有效。这意味着对 Popen
调用使用 universal_newlines=True
确实使用 return 由 locale.getpreferredencoding()
编辑的编码,但是,这是解码 CLI 输出的错误编码。
解决方法是:
当使用带有 universal_newslines=True
的 Popen
调用时,会强制对返回的 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 缺少。