TypeError: при записі до файлу в Python3 потрібен об’єкт, подібний до байтів, а не 'str'


590

Я зовсім недавно перейшов на 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, але ця маленька заява мене клопоче.


11
Чому ви відкриваєте файл у двійковому режимі, але трактуєте його як текст?
Martijn Pieters

4
@MartijnPieters дякуємо, що помітили відкритий режим файлу! Змінивши його в текстовому режимі, вирішив проблему ... код працював надійно в Py2k протягом багатьох років, хоча ...
masroore


10
Я також стикаюся з цим, коли у мене є запити, result = requests.getі я намагаюся x = result.content.split("\n"). Я трохи збентежений повідомленням про помилку, оскільки, здається, це означає, що result.contentце рядок і .split()вимагає байтового об'єкта .. ?? ("потрібен об'єкт, подібний до байтів, а не" str "') ..

Відповіді:


553

Ви відкрили файл у двійковому режимі:

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


12
Якщо ви зазирнете до різних документів, з якими пов’язані ppl, ви побачите, що в Py2 все "працювало", тому що рядки за замовчуванням були байтами, тоді як у Py3 типовими рядками є Unicode, це означає, що будь-коли ви робите введення / виведення, особливо мережа, байтові рядки - це стандарт, тому ви повинні навчитися переміщати b / w Unicode & bytes рядки (en / decode). Для файлів тепер у нас є "r" проти "rb" (і для 'w' & 'a "), щоб допомогти розрізняти.
wescpy

3
@wescpy: Python 2 має 'r'проти 'rb' занадто , перемикання між двійковими і текстовими файлами поведінки (наприклад , переводячи рядки і на деяких платформах, як лікується EOF маркер). Те, що ioбібліотека (що забезпечує функціональність вводу / виводу за замовчуванням у Python 3, але також доступна в Python 2), тепер також декодує текстові файли за замовчуванням, є реальною зміною.
Martijn Pieters

2
@MartijnPieters: Так, домовились. У 2.x я використовував 'b'прапор лише тоді, коли мені довелося працювати з бінарними файлами в DOS / Windows (оскільки двійкові - це типовий POSIX). Добре, що при використанні ioв 3.x для доступу до файлів є подвійна мета .
wescpy

208

Ви можете кодувати рядок, використовуючи .encode()

Приклад:

'Hello World'.encode()

48

Як вже було сказано, ви читаєте файл у двійковому режимі, а потім створюєте список байтів. У наступному для циклу ви порівнюєте рядок з байтами, і саме там код не вдається.

Розшифровка байтів при додаванні до списку має працювати. Змінений код повинен виглядати так:

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'>

25

Ви повинні перейти з 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


15

для цього невеликого прикладу: імпорт сокета

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 'вирішило мою проблему


11

Використовуйте функцію encode () разом із твердо кодованим значенням String, наведеним в одній цитаті.

Наприклад:

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

АБО

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

8

Ви відкрили файл у двійковому режимі:

Наступний код видасть 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')

4

чому б не спробувати відкрити файл як текст?

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

Якщо ви справді намагаєтесь обробити це як двійковий файл, то розгляньте кодування рядка.


1

Я отримав цю помилку, коли я намагався перетворити 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при кодуванні байтів, щоб вирішити проблему.

Сподіваюся, що це комусь допоможе.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.