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


69

我一直在寻找一种从python脚本运行外部进程并在执行期间打印其stdout消息的方法。
下面的代码有效,但是在运行时不输出标准输出。退出时,出现以下错误:

sys.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

我正在使用python 3.3.2


Answers:


114

Python 3处理字符串有些不同。最初,字符串只有一种类型:str。上世纪90年代,当unicode受到人们的青睐时,新的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)自动更改此 类型。

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

当然,UTF-8可能不是您所用的正确字符集,因此请确保使用正确的字符集。

在特定的一段代码,nextline是一个类型bytes,而不是str,阅读stdout以及stdin从subprocess来自在Python 3改str到 bytes。这是因为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 ChangeLog,PEP 358和PEP 3112。


1使用ASCII可以完成一些巧妙的技巧,而使用多字节字符集则无法做到;最著名的例子是“带有空格的xor切换大小写”(例如chr(ord('a') ^ ord(' ')) == 'A')和“设置第6位以构成控制字符”(例如ord('\t') + ord('@') == ord('I'))。ASCII是在处理单个位的操作对性能的影响不可忽略的时候设计的。

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


提示+1!顺便说一句,我看到我只是在程序结束时才得到标准输出。你能告诉我为什么它不能实时打印吗?
— Michael IV

在不知道demo.exe输出什么以及您到底想要什么的情况下,很难回答您的问题,因为不清楚问题是什么:-)但是请注意,它readline()读一行,也就是说,它一直读直到遇到换行符,其中,在Windows上,我相信,\r\n而不是\n
— 马丁Tournoij

我要做的是在python中运行时打印demo.exe stdout消息。当前,python仅在子进程终止时才打印demo.exe stdout
— Michael IV

@MichaelIV不知道为什么您没有任何输出。我测试了它,对我有用。我也曾在Windows上对其进行过测试以确保。确保demo.exe输出\r\n行尾,并且demo.exe也刷新标准输出。
— 马丁·图尔诺伊

32

如果使用sys.stdout.encoding(或兼容的编码,例如从输出ASCII的工具读取且stdout使用UTF-8的兼容编码)对子进程中的字节进行编码,则可接受的答案会很好地工作,这是将任意字节写入stdout的正确方法是:

sys.stdout.buffer.write(some_bytes_object)

这只会按原样输出字节,而不会尝试将它们视为“文本编码”。


作为迁移的一部分,我会将此答案视为正确的答案。例如,我无法更改csv模块写入其数据的方式。因此,我认为最好的方法就是按原样传递数据。感谢您的作品!
— davidmpaz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.