TypeError: требуется байтовый объект, а не str при записи в файл в Python3

736

Я совсем недавно перешел на Py 3.5. Этот код правильно работал в Python 2.7:

with open(fname, 'rb') as f:
    lines = [x.strip() for x in f.readlines()]

for line in lines:
    tmp = line.strip().lower()
    if 'some-pattern' in tmp: continue
    # ... code

После обновления до 3.5 я получаю:

TypeError: a bytes-like object is required, not 'str'

ошибка в последней строке (код поиска по шаблону).

Я пробовал использовать .decode()функцию по обе стороны от оператора, также пробовал:

if tmp.find('some-pattern') != -1: continue

- но безрезультатно.

Мне удалось быстро решить почти все проблемы 2: 3, но это небольшое утверждение меня беспокоит.

4
  • 14
    Почему вы открываете файл в двоичном режиме, но обрабатываете его как текст? Martijn Pieters 10 окт.
  • 6
    @MartijnPieters, спасибо, что заметили режим открытия файла! Перевод в текстовый режим решил проблему ... код надежно работал в Py2k в течение многих лет ...masroore 10 окт.
  • 4
  • 12
    Я тоже сталкиваюсь с этим, когда у меня есть просьбы, result = requests.getи я пытаюсь x = result.content.split("\n"). Я немного смущен сообщением об ошибке, потому что оно, кажется, подразумевает, что result.contentэто строка и .split()требует объект, подобный байтам .. ?? ("требуется объект, похожий на байты, а не str") ..user4805123 25 фев '17 в 18:04
672

Вы открыли файл в двоичном режиме:

with open(fname, 'rb') as f:

Это означает, что все данные, считанные из файла, возвращаются как bytesобъекты, а не str. Затем вы не можете использовать строку в тесте на сдерживание:

if 'some-pattern' in tmp: continue

Вместо этого вам нужно будет использовать bytesобъект для тестирования tmp:

if b'some-pattern' in tmp: continue

или откройте файл как текстовый файл, заменив 'rb'режим на 'r'.

6
  • 17
    Если вы посмотрите на различные документы, на которые ссылается ppl, вы увидите, что в Py2 все «работает», потому что строки по умолчанию были байтами, тогда как в Py3 строки по умолчанию - Unicode, что означает, что каждый раз, когда вы выполняете ввод-вывод, особенно сети байтовые строки являются стандартом, поэтому вы должны научиться перемещать ч / б строки Unicode и байты (en / decode). Для файлов у нас теперь есть «r» и «rb» (и для «w» и «a»), чтобы помочь различать. wescpy 06 мар.
  • 5
    @wescpy: Python 2 имеет 'r'против 'rb' слишком , переключение между двоичными и текстовыми файлами поведения (например , переводя строки и на некоторых платформах, как лечится EOF маркер). Реальным изменением является то, что ioбиблиотека (предоставляющая функции ввода-вывода по умолчанию в Python 3, но также доступная в Python 2) теперь также декодирует текстовые файлы по умолчанию. Martijn Pieters 06 мар.
  • 2
    @MartijnPieters: Да, согласен. В 2.x я использовал этот 'b'флаг только при работе с двоичными файлами в DOS / Windows (поскольку двоичный файл является стандартом POSIX по умолчанию). Хорошо, что при использовании ioв 3.x для доступа к файлам существует двойная цель . wescpy 7 марта '17 в 2:14
  • 2
    ZipFile.open()Документы @ericOnline явно заявляют, что поддерживается только двоичный режим ( доступ к члену архива как к двоичному объекту, подобному файлу ). Вы можете обернуть файловый объект, io.TextIOWrapper()чтобы добиться того же эффекта. Martijn Pieters 7 янв в 22:01
  • 1
    @ericOnline также не используйте, .readlines()если вы можете перебирать объект файла напрямую. Особенно, когда вам нужна информация только из одной строки. Зачем читать все в память, если эту информацию можно найти в первом буферизованном блоке? Martijn Pieters 7 янв в 22:12
278

Вы можете закодировать свою строку, используя .encode()

Пример:

'Hello World'.encode()
1
  • 3
    Этот комментарий был весьма полезен в контексте использования fd.subprocess.Popen(); fd.communicate(...);. jma 8 июня в 13:26
62

Как уже упоминалось, вы читаете файл в двоичном режиме, а затем создаете список байтов. В следующем цикле for вы сравниваете строку с байтами, и именно здесь происходит сбой кода.

Расшифровка байтов при добавлении в список должна работать. Измененный код должен выглядеть следующим образом:

with open(fname, 'rb') as f:
    lines = [x.decode('utf8').strip() for x in f.readlines()]

Тип байтов был введен в Python 3, и поэтому ваш код работал в Python 2. В Python 2 не было типа данных для байтов:

>>> s=bytes('hello')
>>> type(s)
<type 'str'>
1
  • 2
    Python 2 действительно имеет тип для байтов, он просто сбивает с толку, strкогда вызывается тип для текстовых строк unicode. В Python 3 они изменили значение strтак, чтобы оно было таким же, как и старый unicodeтип, и переименовали старый strв bytes. Они также удалили кучу случаев, когда он автоматически пытался преобразовать одно в другое. Mark Ransom 11 фев в 18:38
29

Вам нужно перейти с wb на w:

def __init__(self):
    self.myCsv = csv.writer(open('Item.csv', 'wb')) 
    self.myCsv.writerow(['title', 'link'])

к

def __init__(self):
    self.myCsv = csv.writer(open('Item.csv', 'w'))
    self.myCsv.writerow(['title', 'link'])

После его изменения ошибка исчезает, но вы не можете записать в файл (в моем случае). Так в конце концов, у меня нет ответа?

Источник: Как удалить ^ M

Переход на 'rb' приводит к другой ошибке: io.UnsupportedOperation: write

17

для этого небольшого примера:

import socket

mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mysock.connect(('www.py4inf.com', 80))
mysock.send(**b**'GET http://www.py4inf.com/code/romeo.txt HTTP/1.0\n\n')

while True:
    data = mysock.recv(512)
    if ( len(data) < 1 ) :
        break
    print (data);

mysock.close()

добавление буквы "b" перед "GET http://www.py4inf.com/code/romeo.txt HTTP / 1.0 \ n \ n" решило мою проблему

0
15

Используйте функцию encode () вместе с жестко заданным строковым значением, указанным в одинарной кавычке.

Бывший:

file.write(answers[i] + '\n'.encode())

ИЛИ

line.split(' +++$+++ '.encode())
12

Вы открыли файл в двоичном режиме:

Следующий код вызовет ошибку TypeError: требуется объект, подобный байтам, а не str.

for line in lines:
    print(type(line))# <class 'bytes'>
    if 'substring' in line:
       print('success')

Следующий код будет работать - вы должны использовать функцию decode ():

for line in lines:
    line = line.decode()
    print(type(line))# <class 'str'>
    if 'substring' in line:
       print('success')
6

почему бы не попробовать открыть файл как текст?

with open(fname, 'rt') as f:
    lines = [x.strip() for x in f.readlines()]

Кроме того, вот ссылка для python 3.x на официальной странице: https://docs.python.org/3/library/io.html И это открытая функция: https://docs.python.org/3 /library/functions.html#open

Если вы действительно пытаетесь обрабатывать его как двоичный файл, подумайте о кодировании своей строки.

2

Я получил эту ошибку, когда пытался преобразовать char (или строку) в bytes, код был примерно таким с Python 2.7:

# -*- coding: utf-8 -*-
print( bytes('ò') )

Это способ Python 2.7 при работе с символами Unicode.

Это не будет работать с Python 3.6, так bytesкак для кодирования требуется дополнительный аргумент, но это может быть немного сложно, поскольку разные кодировки могут выводить разные результаты:

print( bytes('ò', 'iso_8859_1') ) # prints: b'\xf2'
print( bytes('ò', 'utf-8') ) # prints: b'\xc3\xb2'

В моем случае мне пришлось использовать iso_8859_1при кодировании байтов, чтобы решить проблему.

Надеюсь, это кому-то поможет.

1
  • 1
    Обратите внимание, что codingкомментарий в верхней части файла не влияет на способ bytesили encodeработу, он только изменяет способ интерпретации символов в вашем исходном коде Python. Mark Ransom 11 февраля в 18:43