用变音符号解码cp1252 stdout

时间:2019-12-05 14:25:37

标签: python character-encoding 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

我茫然为什么无法完成翻译。

注意:

  • 我知道使用universal_newslines=True进行Popen通话时,可以规避整个测试过程,但这使我得以分析编码问题所在。
  • 从Python 3.5开始,似乎Popen构造函数添加了encoding=选项,也许这可以避免类似的问题。可悲的是,Python 3.4是某些LTS Linux版本中包含的版本。

编辑1:这里有些事。尽管Python 3.4文档指出Windows上的默认编码为cp1252,但是在CLI上运行命令并重定向到文件时,该文件在IBM850中进行了编码。最好的猜测是locale.getpreferredencoding()不返回CLI的编码。

编辑2:使用ibm850对标准输出进行解码的过程就像一个超级按钮。这意味着对universal_newlines=True调用使用Popen会使用locale.getpreferredencoding()返回的编码,但是,这是解码CLI输出的错误编码。

1 个答案:

答案 0 :(得分:0)

解决方案是:

universal_newslines=True中使用locale.getpreferredencoding()调用时,将强制对返回的stdout stderr数据进行字符串解释。但是,解释是使用cp1252返回的编码完成的,本例中为cp1252。但是,命令行上的数据不是使用ibm850(类似于Windows的ANSI样式编码)进行编码,而是使用ü(DOS编码)进行编码。因为解码是对字节和int进行运算,并且因为cp1252解码表中str.decode('ibm850')(0x81)的stdout中的int没有对应的值,所以抛出了Python深度的异常。

使用正确解码的Popen手动解码命令行数据。

注意:从Python 3.5开始, so = StackOverflow(key=input("enter key:"), value=input("enter value:")) 允许传递用于数据流的编码,而Python 3.4缺少该编码。