Erro CSV do Python: a linha contém byte NULL

102

Estou trabalhando com alguns arquivos CSV, com o seguinte código:

reader = csv.reader(open(filepath, "rU"))
try:
    for row in reader:
        print 'Row read successfully!', row
except csv.Error, e:
    sys.exit('file %s, line %d: %s' % (filename, reader.line_num, e))

E um arquivo está gerando este erro:

file my.csv, line 1: line contains NULL byte

O que eu posso fazer? O Google parece sugerir que pode ser um arquivo do Excel que foi salvo como .csv indevidamente. Existe alguma maneira de contornar esse problema em Python?

== ATUALIZAÇÃO ==

Seguindo o comentário de @JohnMachin abaixo, tentei adicionar estas linhas ao meu script:

print repr(open(filepath, 'rb').read(200)) # dump 1st 200 bytes of file
data = open(filepath, 'rb').read()
print data.find('\x00')
print data.count('\x00')

E esta é a saída que obtive:

'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1\x00\x00\x00\x00\x00\x00\x00\x00\ .... <snip>
8
13834

Portanto, o arquivo realmente contém bytes NUL.

AP257
fonte
Qual é od -ca aparência da primeira linha?
Ignacio Vazquez-Abrams
que consulta devo executar, algo como cat my.csv | od -c | Mais ? com isso eu obtenho: 0000000 D epartment F amil
AP257
Como o CSV é gerado? No Excel, você pode tentar um dialeto. Caso contrário, veja say: stackoverflow.com/questions/2753022/…
dr jimbob
Obrigado. Não é meu CSV e, infelizmente, não tenho o poder de alterá-lo. Acho que foi criado como Excel e salvo como CSV (boo). Um dialeto parece uma boa ideia - vou tentar isso!
AP257 de
Se estiver realmente salvo como CSV, deve funcionar. Uma coisa que às vezes encontro são arquivos TSV (separados por tabulação) mascarados como CSV, então você pode tentar definir um delimitador de '\ t'. Se ele foi salvo como um arquivo Excel e a extensão mudou para CSV, nenhum dialeto vai funcionar. Acho que sua única opção nesse caso seria usar o Excel para salvar cópias como CSV adequado.
Thomas K

Respostas:

104

Como @ S.Lott diz, você deve abrir seus arquivos no modo 'rb', não no modo 'rU'. No entanto, isso pode NÃO estar causando o problema atual. Tanto quanto eu sei, usar o modo 'rU' iria confundi-lo se houver \rdados incorporados , mas não causaria quaisquer outros dramas. Também observo que você possui vários arquivos (todos abertos com 'rU' ??), mas apenas um está causando o problema.

Se o módulo csv diz que você tem um byte "NULL" (mensagem boba, deveria ser "NUL") em seu arquivo, então você precisa verificar o que está em seu arquivo. Eu sugiro que você faça isso mesmo se usar 'rb' resolver o problema.

repr()é (ou quer ser) seu amigo na depuração. Ele mostrará de forma inequívoca o que você tem, de uma forma independente da plataforma (o que é útil para auxiliares que não sabem o que odé ou o que faz). Faça isso:

print repr(open('my.csv', 'rb').read(200)) # dump 1st 200 bytes of file

e copie / cole cuidadosamente (não digite novamente) o resultado em uma edição de sua pergunta (não em um comentário).

Observe também que se o arquivo for realmente duvidoso, por exemplo, no \ r ou \ n dentro de uma distância razoável do início do arquivo, o número da linha relatado por reader.line_numserá (inutilmente) 1. Encontre onde está o primeiro \x00(se houver) fazendo

data = open('my.csv', 'rb').read()
print data.find('\x00')

e certifique-se de despejar pelo menos essa quantidade de bytes com repr ou od.

O que data.count('\x00')isso te diz? Se houver muitos, você pode querer fazer algo como

for i, c in enumerate(data):
    if c == '\x00':
        print i, repr(data[i-30:i]) + ' *NUL* ' + repr(data[i+1:i+31])

para que você possa ver os bytes NUL no contexto.

Se você pode ver \x00na saída (ou \0na sua od -csaída), então você definitivamente tem byte (s) NUL no arquivo e precisará fazer algo assim:

fi = open('my.csv', 'rb')
data = fi.read()
fi.close()
fo = open('mynew.csv', 'wb')
fo.write(data.replace('\x00', ''))
fo.close()

A propósito, você olhou para o arquivo (incluindo as últimas linhas) com um editor de texto? Ele realmente se parece com um arquivo CSV razoável como os outros arquivos (sem exceção de "byte NULL")?

John Machin
fonte
Muito obrigado por esta ajuda muito detalhada. Existem muitos caracteres \ x00 no arquivo (consulte a edição da pergunta) - é estranho, porque em um editor de texto parece um arquivo CSV perfeitamente razoável.
AP257 de
1
@ AP257: '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1é a "assinatura" que denota um arquivo de Documento Composto OLE2 - por exemplo, um arquivo .XLS do Excel 97-2003 . Acho que "em um editor de texto parece um arquivo CSV perfeitamente razoável", totalmente inacreditável . Você deve ter olhado um arquivo diferente, um arquivo CSV válido, em outra pasta ou em outra máquina ou em algum outro momento. Observe que sua odsaída não foi de um arquivo XLS.
John Machin de
8
@ AP257: Algum motivo específico para você não ter aceitado esta resposta?
John Machin
Funciona, mas deve ser possível e rápido com um objeto semelhante a um arquivo que filtra o CSV e pode ser transmitido csv.readerdiretamente.
gerrit de
1
Não deveria fo.write(data.replace('\x00', ''))ser fo.write(data.replace(b'\x00', b''))? Python 3.6 aqui ...
Boern
23
data_initial = open("staff.csv", "rb")
data = csv.reader((line.replace('\0','') for line in data_initial), delimiter=",")

Isso funciona para mim.

Duplo
fonte
Resolvido para o meu caso, o nulo eram os valores '\ 0'. Obrigado.
Joab Mendes de
19

Ler como UTF-16 também foi meu problema.

Este é o meu código que acabou funcionando:

f=codecs.open(location,"rb","utf-16")
csvread=csv.reader(f,delimiter='\t')
csvread.next()
for row in csvread:
    print row

Onde localização é o diretório do seu arquivo csv.

Do utilizador
fonte
13

Também me deparei com esse problema. Usando o csvmódulo Python , tentei ler um arquivo XLS criado no MS Excel e encontrei o NULL byteerro que você estava obtendo. Eu olhei ao redor e encontrei o módulo xlrd Python para ler e formatar dados de arquivos de planilha do MS Excel. Com o xlrdmódulo, não só consigo ler o arquivo corretamente, mas também posso acessar muitas partes diferentes do arquivo de uma maneira que não conseguia antes.

Achei que poderia te ajudar.

Ayaz
fonte
7
Obrigado por apontar esse módulo. Curiosamente, fui baixá-lo e percebi que o autor era ninguém menos que @John_Machin, que também é o principal comentário sobre essa questão.
Evan
11

Converter a codificação do arquivo de origem de UTF-16 para UTF-8 resolve meu problema.

Como converter um arquivo para utf-8 em Python?

import codecs
BLOCKSIZE = 1048576 # or some other, desired size in bytes
with codecs.open(sourceFileName, "r", "utf-16") as sourceFile:
    with codecs.open(targetFileName, "w", "utf-8") as targetFile:
        while True:
            contents = sourceFile.read(BLOCKSIZE)
            if not contents:
                break
            targetFile.write(contents)
Patrick Halley
fonte
7

Você poderia apenas embutir um gerador para filtrar os valores nulos se quiser fingir que eles não existem. É claro que isso pressupõe que os bytes nulos não façam parte da codificação e sejam algum tipo de artefato ou bug errôneo.

with open(filepath, "rb") as f:
    reader = csv.reader( (line.replace('\0','') for line in f) )

    try:
        for row in reader:
            print 'Row read successfully!', row
    except csv.Error, e:
        sys.exit('file %s, line %d: %s' % (filename, reader.line_num, e))
woot
fonte
2

Por que você está fazendo isso?

 reader = csv.reader(open(filepath, "rU"))

Os documentos são bem claros de que você deve fazer isso:

with open(filepath, "rb") as src:
    reader= csv.reader( src )

O modo deve ser "rb" para ler.

http://docs.python.org/library/csv.html#csv.reader

Se csvfile for um objeto de arquivo, ele deve ser aberto com o sinalizador 'b' nas plataformas onde isso faz diferença.

S.Lott
fonte
@ AP257: "Não ajuda"? Significa o que? Alguma mensagem de erro específica?
S.Lott de
1
@ S.Lott: Significa que ele obteve a mesma resposta de antes. A realidade é que ele está lidando com um arquivo camaleão ou metamorfo ... quando ele despeja odou olha em um editor de texto, parece um arquivo CSV perfeitamente normal. No entanto, quando ele despeja os primeiros bytes com Python repr (), isso se torna um arquivo .XLS do Excel (que foi renomeado para ter uma extensão CSV).
John Machin de
@John Machin: "um arquivo .XLS do Excel (que foi renomeado para ter uma extensão CSV" Faz sentido que não possa ser processado.
S.Lott
1
@ S.Lott: Com esse conteúdo, faz sentido que o módulo csv não possa processá-lo; entretanto, o módulo xlrd pode processá-lo. Sensivelmente, nenhum dos módulos infere nada do nome do arquivo de entrada, se de fato a entrada for um arquivo com um nome.
John Machin de
1
@John Machin: "nenhum módulo infere nada do nome do arquivo de entrada". Verdade. Minha estrutura de aplicativo depende desse fato. Não acreditamos que o nome do arquivo signifique algo, já que as pessoas cometem erros ("mentem"). Portanto, temos que verificar um monte de alternativas até um clique.
S.Lott
2

aparentemente é um arquivo XLS e não um arquivo CSV conforme http://www.garykessler.net/library/file_sigs.html confirmar

Xavier Combelle
fonte
Não necessariamente, mas sim, isso pode ser uma causa. Recebi este erro quando tentei analisar um arquivo CSV que foi salvo pelo Excel de um arquivo XLSX.
Cerin
Com este número mágico é a causa do XLSX ter um número mágico diferente
Xavier Combelle
2

Em vez do leitor csv, uso ler arquivo e dividir a função para string:

lines = open(input_file,'rb') 

for line_all in lines:

    line=line_all.replace('\x00', '').split(";")
Nico The Brush
fonte
1

Eu tenho o mesmo erro. Salvei o arquivo em UTF-8 e funcionou.

mikaiscute
fonte
1
Você pode ter recebido a mesma mensagem de erro, mas a causa seria diferente - provavelmente você o salvou originalmente como UTF-16 (o que o Bloco de notas chama de "Unicode").
John Machin
1

Isso aconteceu comigo quando criei um arquivo CSV com o OpenOffice Calc. Isso não aconteceu quando criei o arquivo CSV no meu editor de texto, mesmo que mais tarde o tenha editado com o Calc.

Resolvi meu problema copiando e colando no meu editor de texto os dados do meu arquivo criado pelo Calc em um novo arquivo criado pelo editor.

user1990371
fonte
1

Eu tive o mesmo problema ao abrir um CSV produzido a partir de um webservice que inseriu bytes NULL em cabeçalhos vazios. Fiz o seguinte para limpar o arquivo:

with codecs.open ('my.csv', 'rb', 'utf-8') as myfile:
    data = myfile.read()
    # clean file first if dirty
    if data.count( '\x00' ):
        print 'Cleaning...'
        with codecs.open('my.csv.tmp', 'w', 'utf-8') as of:
            for line in data:
                of.write(line.replace('\x00', ''))

        shutil.move( 'my.csv.tmp', 'my.csv' )

with codecs.open ('my.csv', 'rb', 'utf-8') as myfile:
    myreader = csv.reader(myfile, delimiter=',')
    # Continue with your business logic here...

Isenção de responsabilidade: esteja ciente de que isso substitui seus dados originais. Certifique-se de ter uma cópia de backup dele. Você foi avisado!

Matthias Kuhn
fonte
0

Para todos os inimigos do modo de arquivo 'rU': Acabei de tentar abrir um arquivo CSV de uma máquina Windows em um Mac com o modo de arquivo 'rb' e recebi este erro do módulo csv:

Error: new-line character seen in unquoted field - do you need to 
open the file in universal-newline mode?

Abrir o arquivo no modo 'rU' funciona bem. Eu amo o modo de nova linha universal - ele me economiza muito trabalho.

Bill Gross
fonte
0

Eu encontrei isso ao usar o scrapy e buscar um csvfile compactado sem ter um middleware correto para descompactar o corpo da resposta antes de entregá-lo ao csvreader. Portanto, o arquivo não era realmente um arquivo csv e gerava o line contains NULL byteerro de acordo.

Gesias
fonte
0

Você já tentou usar gzip.open?

with gzip.open('my.csv', 'rb') as data_file:

Eu estava tentando abrir um arquivo que havia sido compactado, mas tinha a extensão '.csv' em vez de 'csv.gz'. Esse erro continuou aparecendo até eu usar gzip.open

Munene iUwej Julius
fonte
-1

Um caso é este - se o arquivo CSV contiver linhas vazias, esse erro pode aparecer. É necessário verificar a linha antes de prosseguir com a escrita ou leitura.

for row in csvreader:
        if (row):       
            do something

Resolvi meu problema adicionando este cheque no código.

kirancodificar
fonte