Python 3 TypeError: 必须是 str,而不是 sys.stdout.write()的字节

我正在寻找一种方法来从 python 脚本运行一个外部进程,并在执行期间打印它的 stdout 消息。
下面的代码可以工作,但是在运行时没有打印标准输出:

Write (nextline) TypeError: 必须是 str,而不是字节

p = subprocess.Popen(["demo.exe"],stdout = subprocess.PIPE, stderr= subprocess.PIPE)
# Poll process for new output until finished
while True:
nextline = p.stdout.readline()
if nextline == '' and p.poll() != None:
break
sys.stdout.write(nextline)
sys.stdout.flush()


output = p.communicate()[0]
exitCode = p.returncode

我使用的是 python3.3.2

186674 次浏览

Python3处理字符串的方式稍有不同 字符串: str。当 Unicode 在90年代获得关注时,新的 unicode类型 添加到处理 Unicode 而不破坏预先存在的代码 1 有效地与 str相同,但支持多字节。

在 Python 3中有两种不同的类型:

  • bytes类型。这只是一个字节序列,Python 不知道 任何关于如何将其解释为字符的东西。
  • str类型。这也是一个字节序列,< em > 但 Python 知道如何 将这些字节解释为字符 .
  • 单独的 unicode类型被删除。 str现在支持 unicode。

在 Python2中,隐式地假设编码可能会导致许多问题; 可能最终使用错误的编码,或数据可能没有一个编码在 所有(例如,它是一个 PNG 图像)。
明确地告诉 Python 使用哪种编码(或者明确地告诉 Python 使用哪种编码) 通常要好得多,而且更符合“ Python 哲学” 「 显式比隐式好」。

这个变化与 Python2不兼容,因为许多返回值都发生了变化, 导致像这样的微妙问题,这可能是主要原因 Python 3的采用非常缓慢,因为 Python 没有静态类型 2 不可能用一个脚本(比如绑定的 2to3).

  • 可以使用 bytes('h€llo', 'utf-8')str转换为 bytes; 这应该 产生 b'H\xe2\x82\xacllo'。注意一个字符是如何转换为三个字符的 字节。
  • 你可以用 b'H\xe2\x82\xacllo'.decode('utf-8')bytes转换成 str

当然,在您的情况下,UTF-8可能不是正确的字符集,所以一定要确定 使用正确的方法。

在您的特定代码段中,nextlinebytes类型,而不是 str类型, 从 subprocess读取 stdoutstdin在 Python3中从 str改为 这是因为 Python 无法确定它使用的是哪种编码 可能 使用与 sys.stdin.encoding(系统的编码)相同的方法, 但不能确定。

你需要更换:

sys.stdout.write(nextline)

与:

sys.stdout.write(nextline.decode('utf-8'))

或者也许:

sys.stdout.write(nextline.decode(sys.stdout.encoding))

您还需要将 if nextline == ''修改为 if nextline == b'',因为:

>>> '' == b''
False

另请参阅 Python 3 ChangeLogPEP 358PEP 3112


1 对于 ASCII,有一些巧妙的技巧,但是对于多字节字符集就不行了; 最著名的例子是“ xor with space to switch case”(例如 chr(ord('a') ^ ord(' ')) == 'A')和“ set 6th bit to make a control string”(例如 ord('\t') + ord('@') == ord('I'))。ASCII 设计的时候,操纵单个比特对性能的影响是不可忽视的。

2 是的,你可以使用函数注释,但是它是一个相对较新的特性,很少被使用。

如果子进程中的字节使用 sys.stdout.encoding编码(或者使用兼容的编码,比如从输出 ASCII 的工具中读取数据,而 stdout 使用 UTF-8) ,那么可以接受的答案可以很好地工作,但是将任意字节写入 stdout 的正确方法是:

sys.stdout.buffer.write(some_bytes_object)

这只是按原样输出字节,而不会试图将它们视为某种编码中的文本。