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

Mic*_* IV 56 python

我一直在寻找一种从python脚本运行外部进程的方法,并在执行期间打印其stdout消息.
下面的代码可以工作,但在运行时打印没有stdout输出.当它退出时,我收到以下错误:

sys.stdout.write(nextline)TypeError:必须是str,而不是bytes

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
Run Code Online (Sandbox Code Playgroud)

我使用的是python 3.3.2

Mar*_*oij 95

Python 3处理字符串有点不同.最初只有一种类型的字符串:str.当unicode在90年代获得牵引力时,新unicode类型被添加到处理Unicode而不破坏预先存在的代码1.这实际上str与多字节支持相同.

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

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

在Python 2中隐式假设编码可能会导致很多问题; 您可能最终使用错误的编码,或者数据可能根本没有编码(例如,它是PNG图像).
明确地告诉Python使用哪种编码(或明确地告诉它猜测)通常要好得多,并且更符合" 显式优于隐式 " 的"Python哲学".

这种变化与Python 2不兼容,因为许多返回值已经改变,导致像这样的微妙问题; 这可能是Python 3采用速度如此之慢的主要原因.由于Python没有静态类型2, 因此无法使用脚本(例如捆绑2to3)自动更改此内容 .

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

当然,在您的情况下,UTF-8可能不是正确的字符集,因此请务必使用正确的字符集.

在特定的一段代码,nextline是一个类型bytes,而不是str,阅读stdout以及stdinsubprocess来自在Python 3改strbytes.这是因为Python无法确定使用哪种编码.它 可能sys.stdin.encoding(系统的编码)相同,但不能确定.

你需要替换:

sys.stdout.write(nextline)
Run Code Online (Sandbox Code Playgroud)

有:

sys.stdout.write(nextline.decode('utf-8'))
Run Code Online (Sandbox Code Playgroud)

或者可能:

sys.stdout.write(nextline.decode(sys.stdout.encoding))
Run Code Online (Sandbox Code Playgroud)

您还需要修改if nextline == ''if nextline == b'':

>>> '' == b''
False
Run Code Online (Sandbox Code Playgroud)

另请参阅Python 3 ChangeLog,PEP 358PEP 3112.


1使用ASCII可以做一些巧妙的技巧,你无法使用多字节字符集; 最着名的例子是"xor with space to switch case"(例如chr(ord('a') ^ ord(' ')) == 'A')和"set 6th bit to a control a character"(例如ord('\t') + ord('@') == ord('I')).ASCII的设计是在操纵单个位是一个具有不可忽略的性能影响的操作的时候.

2是的,您可以使用功能注释,但它是一个相对较新的功能,很少使用.


Woo*_*ble 26

如果你从子进程中获得的字节是使用sys.stdout.encoding(或兼容的编码,如从输出ASCII的工具读取而你的stdout使用UTF-8)编码接受的答案将正常工作,那么将任意字节写入stdout的正确方法是:

sys.stdout.buffer.write(some_bytes_object)
Run Code Online (Sandbox Code Playgroud)

这将按原样输出字节,而不是试图将它们视为一些文本编码.