TypeError: um objeto semelhante a bytes é necessário, não 'str' ao gravar em um arquivo em Python3

590

Eu mudei recentemente para o Py 3.5. Este código estava funcionando corretamente no 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

Depois de atualizar para o 3.5, estou recebendo o:

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

erro na última linha (o código de pesquisa padrão).

Eu tentei usar a .decode()função em ambos os lados da instrução, também tentei:

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

- para nenhum proveito.

Consegui resolver quase todos os problemas 2: 3 rapidamente, mas esta pequena declaração está me incomodando.

masroore
fonte
11
Por que você está abrindo o arquivo no modo binário, mas o trata como texto?
Martijn Pieters
4
@MartijnPieters obrigado por detectar o modo de abertura de arquivo! Alterá-lo para o modo de texto resolvido a questão ... o código tinha trabalhado de forma confiável em Py2k por muitos anos embora ...
masroore
10
Também estou encontrando isso onde tenho solicitações result = requests.gete tento x = result.content.split("\n"). Estou um pouco confuso com a mensagem de erro, porque parece implicar que result.contenté uma string e .split()está exigindo um objeto semelhante a bytes .. ?? ( "Bytes de um objecto semelhante é necessária, não 'str"') ..

Respostas:

553

Você abriu o arquivo no modo binário:

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

Isso significa que todos os dados lidos no arquivo são retornados como bytesobjetos, não str. Você não pode usar uma sequência em um teste de contenção:

if 'some-pattern' in tmp: continue

Você precisaria usar um bytesobjeto para testar tmp:

if b'some-pattern' in tmp: continue

ou abra o arquivo como um arquivo de texto substituindo o 'rb'modo por 'r'.

Martijn Pieters
fonte
12
Se você der uma olhada nos vários documentos aos quais as pessoas estão vinculadas, verá que tudo "funcionou" no Py2 porque as strings padrão eram bytes, enquanto no Py3, as strings padrão são Unicode, o que significa que sempre que você estiver executando E / S, esp. rede, as cadeias de bytes são o padrão; portanto, você deve aprender a mover as cadeias de caracteres Unicode e bytes em preto e branco (en / decode). Para arquivos, agora temos "r" vs. "rb" (e para 'w' e 'a') para ajudar a diferenciar.
Wescpy 6/03/19
3
@wescpy: Python 2 tem 'r'vs 'rb' também , alternando entre os comportamentos dos arquivos binários e texto (como novas linhas traduzindo e em certas plataformas, como o marcador EOF é tratada). Que a iobiblioteca (fornecendo a funcionalidade de E / S padrão no Python 3, mas também disponível no Python 2) agora também decodifique arquivos de texto por padrão é a mudança real.
Martijn Pieters
2
@MartijnPieters: Sim, concordou. Na 2.x, eu usei o 'b'sinalizador apenas quando tive que trabalhar com arquivos binários no DOS / Windows (como binário é o padrão do POSIX). É bom que haja um objetivo duplo ao usar o io3.x para acesso a arquivos.
Wescpy
209

Você pode codificar sua string usando .encode()

Exemplo:

'Hello World'.encode()
theofpa
fonte
48

Como já foi mencionado, você está lendo o arquivo no modo binário e criando uma lista de bytes. No loop for a seguir, você está comparando string com bytes e é aí que o código está falhando.

Decodificar os bytes ao adicionar à lista deve funcionar. O código alterado deve ter a seguinte aparência:

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

O tipo de bytes foi introduzido no Python 3 e é por isso que seu código funcionou no Python 2. No Python 2 não havia tipo de dados para bytes:

>>> s=bytes('hello')
>>> type(s)
<type 'str'>
Suresh
fonte
25

Você precisa mudar de wb para w:

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

para

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

Depois de alterar isso, o erro desaparece, mas você não pode gravar no arquivo (no meu caso). Então, afinal, eu não tenho uma resposta?

Fonte: Como remover ^ M

Alterar para 'rb' gera outro erro: io.UnsupportedOperation: write

meck373
fonte
15

para este pequeno exemplo: soquete de importação

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

adicionar o "b" antes de 'GET http://www.py4inf.com/code/romeo.txt HTTP / 1.0 \ n \ n' resolver meu problema

iniciante
fonte
11

Use a função encode () junto com o valor de String codificado, fornecido em uma única citação.

Ex:

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

OU

line.split(' +++$+++ '.encode())
Shiv Buyya
fonte
8

Você abriu o arquivo no modo binário:

O código a seguir lançará um TypeError: um objeto semelhante a bytes é necessário, não 'str'.

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

O código a seguir funcionará - você deve usar a função decode ():

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

Eu recebi esse erro quando estava tentando converter um caractere (ou string) para bytes, o código era algo parecido com o Python 2.7:

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

Esta é a maneira do Python 2.7 ao lidar com caracteres unicode.

Isso não funcionará com o Python 3.6, pois bytesrequer um argumento extra para codificação, mas isso pode ser um pouco complicado, pois diferentes codificações podem gerar resultados diferentes:

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

No meu caso, eu tive que usar iso_8859_1ao codificar bytes para resolver o problema.

Espero que isso ajude alguém.

Ibrahim.H
fonte