TypeError: wymagany jest obiekt podobny do bajtu, a nie „str” podczas zapisywania do pliku w Python3

590

Niedawno przeprowadziłem migrację do Py 3.5. Ten kod działał poprawnie w Pythonie 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

Po aktualizacji do wersji 3.5 otrzymuję:

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

błąd w ostatnim wierszu (kod wyszukiwania wzoru).

Próbowałem użyć .decode()funkcji po obu stronach instrukcji, próbowałem też:

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

- bez skutku.

Byłem w stanie szybko rozwiązać prawie wszystkie problemy 2: 3, ale to małe stwierdzenie mnie wkurza.

masroore
źródło
11
Dlaczego otwierasz plik w trybie binarnym, ale traktujesz go jako tekst?
Martijn Pieters
4
@MartijnPieters dzięki za wykrycie trybu otwartego pliku! Przejście na tryb tekstowy rozwiązało problem ... kod działał niezawodnie w Py2k od wielu lat ...
masroore,
10
Napotykam to również tam, gdzie mam prośby result = requests.geti staram się x = result.content.split("\n"). Jestem trochę zdezorientowany komunikatem o błędzie, ponieważ wydaje się sugerować, że result.contentjest to ciąg znaków i .split()wymaga obiektu podobnego do bajtów. („wymagany jest obiekt podobny do bajtów, a nie„ str ””) ..

Odpowiedzi:

553

Plik otworzyłeś w trybie binarnym:

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

Oznacza to, że wszystkie dane odczytane z pliku są zwracane jako bytesobiekty, a nie str. Nie można wtedy użyć ciągu w teście ograniczającym:

if 'some-pattern' in tmp: continue

Zamiast tego musisz użyć bytesobiektu do przetestowania tmp:

if b'some-pattern' in tmp: continue

lub zamiast tego otwórz plik jako plik tekstowy, zastępując 'rb'tryb na 'r'.

Martijn Pieters
źródło
12
Jeśli zajrzysz do różnych dokumentów, z którymi łączy się ppl, zobaczysz, że wszystko „działało” w Py2, ponieważ domyślne ciągi były bajtami, podczas gdy w Py3, domyślnymi ciągami są Unicode, co oznacza, że ​​za każdym razem, gdy wykonujesz operacje we / wy, esp. sieci, ciągi bajtów są standardem, więc musisz nauczyć się poruszać czarno-biały ciąg znaków i bajtów (en / dekodować). W przypadku plików mamy teraz „r” vs. „rb” (oraz „w” i „a”), które pomagają odróżnić.
wescpy,
3
@wescpy: Python 2 ma 'r'vs 'rb' też , przełączanie pomiędzy binarne i tekstowe zachowań plików (jak tłumaczeniu nowej linii i na niektórych platformach, jak znacznik EOF jest traktowany). To, że iobiblioteka (zapewniająca domyślną funkcjonalność I / O w Pythonie 3, ale także dostępna w Pythonie 2) teraz domyślnie dekoduje pliki tekstowe, jest prawdziwą zmianą.
Martijn Pieters
2
@MartijnPieters: Tak, uzgodniono. W wersji 2.x używałem 'b'flagi tylko wtedy, gdy musiałem pracować z plikami binarnymi w systemie DOS / Windows (ponieważ plik binarny jest domyślny POSIX). Dobrze, że w celu iouzyskania dostępu do plików ma zastosowanie podwójny cel .
wescpy,
208

Możesz zakodować swój ciąg za pomocą .encode()

Przykład:

'Hello World'.encode()
theofpa
źródło
48

Jak już wspomniano, czytasz plik w trybie binarnym, a następnie tworzysz listę bajtów. W poniższej pętli for porównujesz ciąg znaków do bajtów i właśnie tam kod zawiedzie.

Dekodowanie bajtów podczas dodawania do listy powinno działać. Zmieniony kod powinien wyglądać następująco:

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

Typ bajtów został wprowadzony w Pythonie 3 i dlatego twój kod działał w Pythonie 2. W Pythonie 2 nie było typu danych dla bajtów:

>>> s=bytes('hello')
>>> type(s)
<type 'str'>
Suresh
źródło
25

Musisz zmienić z wb na w:

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

do

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

Po zmianie tego błędu znika, ale nie możesz zapisać do pliku (w moim przypadku). W końcu nie mam odpowiedzi?

Źródło: Jak usunąć ^ M

Zmiana na „rb” powoduje drugi błąd: io.UnsupportedOperation: write

meck373
źródło
15

dla tego małego przykładu: importuj gniazdo

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()

dodanie litery „b” przed „GET http://www.py4inf.com/code/romeo.txt HTTP / 1.0 \ n \ n” rozwiązało mój problem

rozrusznik
źródło
11

Użyj funkcji encode () wraz z zakodowaną wartością ciągu podaną w jednym cudzysłowie.

Dawny:

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

LUB

line.split(' +++$+++ '.encode())
Shiv Buyya
źródło
8

Plik otworzyłeś w trybie binarnym:

Poniższy kod spowoduje wygenerowanie błędu typu: wymagany jest obiekt podobny do bajtu, a nie „str”.

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

Działa następujący kod - musisz użyć funkcji decode ():

for line in lines:
    line = line.decode()
    print(type(line))# <class 'str'>
    if 'substring' in line:
       print('success')
Matan Hugi
źródło
1

Wystąpił ten błąd, gdy próbowałem przekonwertować znak (lub ciąg znaków) bytes, kod był podobny do tego w Pythonie 2.7:

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

Tak działa Python 2.7 w przypadku znaków Unicode.

To nie będzie działać z Pythonem 3.6, ponieważ byteswymaga dodatkowego argumentu do kodowania, ale może to być trochę trudne, ponieważ różne kodowanie może dawać różne wyniki:

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

W moim przypadku musiałem użyć iso_8859_1kodowania bajtów, aby rozwiązać problem.

Mam nadzieję, że to komuś pomoże.

Ibrahim.H
źródło