【问题标题】:Why does opening a subprocess with universal_newlines cause a unicode decode exception?为什么用universal_newlines打开子进程会导致unicode解码异常?
【发布时间】:2015-05-13 16:59:54
【问题描述】:

我正在使用 subprocess 模块运行子作业,并使用 subprocess.PIPE 收集其输出和错误流。为了避免死锁,我不断地在单独的线程上从这些流中读取。这可行,但有时程序会因解码问题而崩溃:

`UnicodeDecodeError:'ascii'编解码器无法解码位置 483 中的字节 0xe2:序数不在范围内(128

在高层次上,我了解 Python 可能正在尝试使用 ASCII 编解码器转换为字符串,并且我需要在某个地方调用 decode,我只是不确定在哪里。当我创建我的子流程作业时,我将universal_newlines 指定为True。我认为这意味着,将 stdout/stderr 作为 unicode,而不是二进制返回:

self.p = subprocess.Popen(self.command, shell=self.shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

崩溃发生在我的阅读线程函数中:

def standardOutHandler(standardOut):
    # Crash happens on the following line:
    for line in iter(standardOut.readline, ''):
       writerLock.acquire()
       stdout_file.write(line)
       if self.echoOutput:
           sys.stdout.write(line)
           sys.stdout.flush()
       writerLock.release()

不清楚为什么 readline 在这里抛出解码异常;正如我所说,我认为universal_newlines 是真的已经返回给我解码的数据。

这里发生了什么,我可以做些什么来纠正这个问题?

这是完整的回溯

Exception in thread Thread-5:
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 920, in _bootstrap_inner
self.run()
  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 868, in run
self._target(*self._args, **self._kwargs)
  File "/Users/lzrd/my_process.py", line 61, in standardOutHandler
for line in iter(standardOut.readline, ''):
  File "/Users/lzrd/Envs/my_env/bin/../lib/python3.4/encodings/ascii.py", line 26, in decode
return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 483: ordinal not in range(128)

【问题讨论】:

  • 它使用locale.getpreferredencoding()返回的任何内容进行解码
  • @PadraicCunningham,我检查了该函数返回的内容,它确实返回了 UTF-8。系统首选编码为:UTF-8。
  • 错误到底发生在哪里?
  • 你自己解码每个line可能会更好,看起来它可能被编码为latin-1,在python 3中你也可以在没有readline或iter的情况下迭代stdout。跨度>
  • @PadraicCunningham 你能详细说明一下答案吗?我不应该在我的子流程设置中使用universal_newlines 吗?如果不在文本模式下查找 \n,那么在 python3 中迭代 stdout 实际上会做什么?谢谢!

标签: python python-3.x unicode subprocess


【解决方案1】:

如果您使用universal_newlines=True,则字节流将使用locale.getpreferredencoding(False) 字符编码解码为Unicode,在您的系统上应该是utf-8(检查LANGLC_CTYPELC_ALL envvars)。

如果异常仍然存在;尝试使用空循环体的代码:

for line in standardOut: #NOTE: no need to use iter() idiom here on Python 3
    pass

如果您仍然遇到异常,那么如果您在Popen() 调用附近检查locale.getpreferredencoding(False) 不是ascii,则它可能是 Python 中的错误 - 在这里使用完全相同的环境很重要。

如果UnicodeDecodeError 显示的是utf-8 而不是ascii,我会理解。在这种情况下,您可以尝试手动解码流:

#!/usr/bin/env python3
import io
import locale
from subprocess import Popen, PIPE

with Popen(['command', 'arg 1'], stdout=PIPE, bufsize=1) as p:
    for line in io.TextIOWrapper(p.stdout,
                                 encoding=locale.getpreferredencoding(False),
                                 errors='strict'): 
        print(line, end='')

您可以在此处试验encodingerrors 参数,例如,设置encoding='ascii' 或使用errors='namereplace' 将不支持的字符(在给定的字符编码中)替换为\N{...} 转义序列(用于调试)。

【讨论】:

    【解决方案2】:

    也许很好:

    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, encoding='utf-8') out, err = process.communicate() print('out: ') print(out) print('err: ') print(err)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多