Подтвердить что ты не робот

Python 3 TypeError: должен быть str, а не байтами с sys.stdout.write()

Я искал способ запустить внешний процесс из python script и распечатать его сообщения stdout во время выполнения.
Приведенный ниже код работает, но не выводит вывод 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

4b9b3361

Ответ 1

Python 3 обрабатывает строки немного иначе. Первоначально был только один тип для строки: str. Когда юникод приобрел сцепление в 90-х годах, новый тип unicode был добавлен для обработки Unicode без нарушения существующего кода 1. Это эффективно, так же как str, но с поддержкой многобайтов.

В Python 3 существуют два разных типа:

  • Тип bytes. Это всего лишь последовательность байтов, Python не знает что-то о том, как интерпретировать это как символы.
  • Тип str. Это также последовательность байтов, но Python знает, как интерпретировать эти байты как символы.
  • Отключен отдельный тип unicode. str теперь поддерживает unicode.

В Python 2 неявно предполагается, что кодирование может вызвать множество проблем; вы может закончиться неправильным кодированием, или данные могут не иметь кодировки в все (например, изображение PNG).
Явно говорю Python, какую кодировку использовать (или явно сообщая догадка) часто намного лучше и намного больше соответствует "философии Питона", из явного лучше, чем неявное.

Это изменение несовместимо с Python 2, поскольку многие возвращаемые значения изменились, приводя к таким тонким проблемам, как этот; это, вероятно, главная причина, почему Принятие Python 3 было настолько медленным. Поскольку Python не имеет статического ввода 2 невозможно автоматически изменить это с помощью script (например, в комплекте 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 Да, вы можете использовать аннотации функций, но это сравнительно новая функция и мало используется.

Ответ 2

В то время как принятый ответ будет работать нормально, если байты, которые у вас есть из вашего подпроцесса, кодируются с помощью sys.stdout.encoding (или совместимая кодировка, например, чтение из инструмента, который выводит ASCII, а ваш stdout использует UTF-8), правильный путь для записи произвольных байтов в stdout:

sys.stdout.buffer.write(some_bytes_object)

Это будет просто выводить байты как есть, не пытаясь рассматривать их как текст в некотором кодировании.

Ответ 3

для записи в файл в Python 3.7.0 сработал приведенный ниже код

f.write(str(YOURFileContent, 'utf-8'))