子进程 "TypeError: a bytes-like object is required, not 'str'"

30

我正在使用这段代码(几年前的一个问题),但我认为这已经过时了。尝试运行该代码,我收到了上面的错误。我在Python方面还是个新手,所以我不能从类似的问题中得到很多解释。有人知道为什么会出现这种情况吗?

import subprocess

def getLength(filename):
  result = subprocess.Popen(["ffprobe", filename],
    stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
  return [x for x in result.stdout.readlines() if "Duration" in x]

print(getLength('bell.mp4'))

回溯(Traceback)

Traceback (most recent call last):
  File "B:\Program Files\ffmpeg\bin\test3.py", line 7, in <module>
    print(getLength('bell.mp4'))
  File "B:\Program Files\ffmpeg\bin\test3.py", line 6, in getLength
    return [x for x in result.stdout.readlines() if "Duration" in x]
  File "B:\Program Files\ffmpeg\bin\test3.py", line 6, in <listcomp>
    return [x for x in result.stdout.readlines() if "Duration" in x]
TypeError: a bytes-like object is required, not 'str'
2个回答

62

subprocess默认返回bytes对象作为标准输出流或标准错误流。这意味着您在操作这些对象时也需要使用bytes对象。"Duration" in x使用了str对象。请使用字节文字(注意前缀b):

return [x for x in result.stdout.readlines() if b"Duration" in x]

如果您知道使用的编码(通常是本地默认值,但您可以设置LC_ALL或更具体的区域环境变量来处理子进程),请先解码数据:

return [x for x in result.stdout.read().decode(encoding).splitlines(True)
        if "Duration" in x]

另一种方法是通过设置适当的编解码器将数据解码为Unicode字符串,让 subprocess.Popen() 进行翻译:

result = subprocess.Popen(
    ["ffprobe", filename],
    stdout=subprocess.PIPE, stderr = subprocess.STDOUT,
    encoding='utf8'
)

如果你设置text=True(Python 3.7及以上版本,在之前的版本中该选项称为universal_newlines),你也可以启用解码,使用你系统默认编解码器,与open()调用使用的相同。在此模式下,默认情况下,管道是按行缓冲的。

可以指出 Python 3.7+ 中的 universal_newlines=Truetext=True,这会导致 Python 将输出解码为系统默认编码的文本并返回一个字符串。 - tripleee
1
@tripleee:已添加。 - Martijn Pieters
Popen的编码参数在Python 3.6中可用,在之前的版本(例如我的情况下是Python 3.5),在进行字节转换时必须明确指定编码(bytes("Duration", encoding='utf8'))。 - adn05

5
就像错误提示所说,“Duration”是一个字符串。而X是一个字节对象,因为results.stdout.readlines()将输出中的行读取为字节码而不是字符串。
因此,将“Duration”存储在一个变量中,比如说str_var,并使用str_var.encode('utf-8')将其编码为一个字节数组对象。
请参考[这里][1]。
[1]: Python 3中将字符串转换为字节的最佳方法是什么?

这只是一个字面值,只需在其前加上 b 即可。您也不需要将字符串存储在变量中才能对其进行编码,"Duration".encode('utf-8') 也可以(但如果您一开始就可以使用 bytes 对象,那么这将浪费计算机周期)。 - Martijn Pieters
好的,如果他想在多个文件中使用它,最好将它存储在一个变量中。现在,请问为什么要对此进行负面评价? - Harshith Thota
为什么?字符串字面值已经作为常量存储在代码对象中了,他们在哪里提到了多个文件? - Martijn Pieters
请注意,测试是在循环中完成的,使用文字字面量更好,因为它会加载一个常量,而不必每次查找变量。 - Martijn Pieters
还好,但是这并不能解释为什么要点踩。这不是一个错误的答案。 - Harshith Thota

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接