подпроцесс TypeError: требуется байтовый объект, а не 'str'

Я использую этот код из ранее заданного вопроса несколько лет назад, однако я считаю, что он устарел. Пытаясь запустить код, я получаю сообщение об ошибке выше. Я все еще новичок в 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 (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'

person chatbottest    schedule 08.07.2017    source источник


Ответы (2)


subprocess по умолчанию возвращает bytes объектов для потоков stdout или stderr. Это означает, что вам также необходимо использовать объекты 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]

Альтернативой является указание subprocess.Popen() декодировать данные в строки Unicode, установив аргумент encoding на подходящий кодек:

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

Если вы установите text=True (Python 3.7 и выше, в предыдущих версиях эта версия называется universal_newlines), вы также включите декодирование, используя ваш кодек системы по умолчанию, тот же, что используется для вызовов open(). В этом режиме каналы буферизуются строками по умолчанию.

person Martijn Pieters    schedule 08.07.2017
comment
Возможно, укажите universal_newlines=True aka text=True в Python 3.7+, который заставляет Python декодировать вывод как текст в кодировке системы по умолчанию и возвращать строку. - person tripleee; 01.04.2019
comment
@tripleee: добавлено. - person Martijn Pieters; 01.04.2019
comment
Аргумент кодирования Popen доступен из Python 3.6, в предыдущей версии (Python 3.5 в моем случае) вы должны уточнить кодировку при преобразовании байтов (bytes("Duration", encoding='utf8')) - person adn05; 05.04.2019

Как говорит ошибка, «Продолжительность» - это строка. Принимая во внимание, что X является байтовым объектом, поскольку results.stdout.readlines() считывает строки в выводе как байт-код, а не строку.

Следовательно, сохраните «Длительность» в переменной, скажем, str_var, и закодируйте ее в объект массива байтов, используя str_var.encode('utf-8').

См. [это] [1].

[1]: Лучший способ преобразовать строку в байты в Питон 3?

person Harshith Thota    schedule 08.07.2017
comment
Это просто литерал, просто добавьте к нему префикс b. Вам не нужно хранить строку в переменной, чтобы иметь возможность ее кодировать, "Duration".encode('utf-8') тоже работает (но это пустая трата компьютерных циклов, если вы можете просто сделать ее байтовым объектом для начала). - person Martijn Pieters; 08.07.2017
comment
Ну а если он хочет использовать его для нескольких файлов, то лучше хранить в переменной. Теперь не могли бы объяснить, почему за это минусуют? - person Harshith Thota; 08.07.2017
comment
Почему? Строковый литерал в любом случае хранится как константа с объектом кода, и где они упоминают несколько файлов? - person Martijn Pieters; 08.07.2017
comment
Обратите внимание, что тест выполняется в цикле, использование литерала здесь лучше, потому что он загружает константу, а не каждый раз ищет переменную. - person Martijn Pieters; 08.07.2017
comment
Достаточно справедливо, но все же не объясняет отрицательное голосование. Это не неправильный ответ. - person Harshith Thota; 08.07.2017