Erreur Python CSV: la ligne contient un octet NULL

102

Je travaille avec des fichiers CSV, avec le code suivant:

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

Et un fichier lève cette erreur:

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

Que puis-je faire? Google semble suggérer qu'il peut s'agir d'un fichier Excel qui n'a pas été correctement enregistré au format .csv. Y a-t-il un moyen de contourner ce problème en Python?

== MISE À JOUR ==

Suite au commentaire de @ JohnMachin ci-dessous, j'ai essayé d'ajouter ces lignes à mon 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')

Et voici le résultat que j'ai obtenu:

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

Le fichier contient donc effectivement des octets NUL.

AP257
la source
À quoi ressemble od -cla première ligne?
Ignacio Vazquez-Abrams
quelle requête dois-je exécuter, quelque chose comme cat my.csv | od -c | plus ? avec ça j'obtiens: 0000000 D epartment F amil
AP257
Comment le CSV est-il généré? Depuis Excel, vous pourrez peut-être essayer un dialecte. Sinon, regardez par exemple: stackoverflow.com/questions/2753022/…
dr jimbob
Merci. Ce n'est pas mon CSV et je n'ai malheureusement pas le pouvoir de le changer. Je pense qu'il a été créé au format Excel et enregistré au format CSV (boo). Un dialecte semble être une bonne idée - je vais essayer!
AP257
S'il a été enregistré au format CSV, cela devrait fonctionner. Une chose que je trouve parfois est que les fichiers TSV (séparés par des tabulations) se font passer pour CSV, vous pouvez donc essayer de définir un délimiteur de «\ t». S'il a été enregistré en tant que fichier Excel et que l'extension a été changée en CSV, aucun dialecte ne fonctionnera. Je pense que votre seule option dans ce cas serait d'utiliser Excel pour enregistrer des copies au format CSV approprié.
Thomas K

Réponses:

104

Comme @ S.Lott le dit, vous devriez ouvrir vos fichiers en mode «rb», pas en mode «rU». Cependant, cela ne cause peut-être PAS votre problème actuel. Pour autant que je sache, l'utilisation du mode «rU» vous dérangerait s'il y avait \rdes données intégrées, mais ne causerait pas d'autres drames. Je note également que vous avez plusieurs fichiers (tous ouverts avec 'rU' ??) mais qu'un seul cause un problème.

Si le module csv indique que vous avez un octet "NULL" (message idiot, devrait être "NUL") dans votre fichier, alors vous devez vérifier ce qu'il y a dans votre fichier. Je vous suggère de le faire même si l'utilisation de «rb» fait disparaître le problème.

repr()est (ou veut être) votre ami de débogage. Il montrera sans ambiguïté ce que vous avez, de manière indépendante de la plate-forme (ce qui est utile aux assistants qui ne savent pas ce qui odest ou fait). Faites ceci:

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

et copiez / collez soigneusement (ne retapez pas) le résultat dans une édition de votre question (pas dans un commentaire).

Notez également que si le fichier est vraiment douteux, par exemple non \ r ou \ n à une distance raisonnable du début du fichier, le numéro de ligne indiqué par reader.line_numsera (inutile) 1. Trouvez où se trouve le premier \x00(le cas échéant) en faisant

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

et assurez-vous de vider au moins autant d'octets avec repr ou od.

Que data.count('\x00')vous dit-il? S'il y en a beaucoup, vous pouvez faire quelque chose comme

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

afin que vous puissiez voir les octets NUL dans leur contexte.

Si vous pouvez voir \x00dans la sortie (ou \0dans votre od -csortie), alors vous avez certainement des octets NUL dans le fichier, et vous devrez faire quelque chose comme ceci:

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

Au fait, avez-vous regardé le fichier (y compris les dernières lignes) avec un éditeur de texte? Cela ressemble-t-il réellement à un fichier CSV raisonnable comme les autres fichiers (pas d'exception "NULL byte")?

John Machin
la source
Merci beaucoup pour cette aide très détaillée. Il y a beaucoup de caractères \ x00 dans le fichier (voir éditer à la question) - c'est étrange, car dans un éditeur de texte, cela ressemble à un fichier CSV parfaitement raisonnable.
AP257
1
@ AP257: '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1est la "signature" désignant un fichier de document composé OLE2 - par exemple un fichier Excel 97-2003 .XLS . Je trouve que "dans un éditeur de texte, cela ressemble à un fichier CSV parfaitement raisonnable" est tout à fait incroyable . Vous devez avoir consulté un fichier différent, un fichier CSV valide, dans un autre dossier ou sur une autre machine ou à un autre moment. Notez que votre odsortie ne provient pas d'un fichier XLS.
John Machin
8
@ AP257: Une raison particulière pour laquelle vous n'avez pas accepté cette réponse?
John Machin
Fonctionne, mais devrait être possible et agréable à la volée avec un objet de type fichier qui filtre le CSV et peut être transmis csv.readerdirectement.
gerrit
1
Ne devrait pas l' fo.write(data.replace('\x00', ''))être fo.write(data.replace(b'\x00', b''))? Python 3.6 here ...
Boern
23
data_initial = open("staff.csv", "rb")
data = csv.reader((line.replace('\0','') for line in data_initial), delimiter=",")

Cela fonctionne pour moi.

double
la source
Résolu pour mon cas, les valeurs nulles étaient les valeurs '\ 0'. Merci.
Joab Mendes
19

Le lire comme UTF-16 était aussi mon problème.

Voici mon code qui a fini par fonctionner:

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

Où emplacement est le répertoire de votre fichier csv.

Utilisateur
la source
13

Je suis tombé sur ce problème aussi. En utilisant le csvmodule Python , j'essayais de lire un fichier XLS créé dans MS Excel et de rencontrer l' NULL byteerreur que vous obteniez. J'ai regardé autour de moi et j'ai trouvé le module xlrd Python pour lire et formater des données à partir de fichiers de feuille de calcul MS Excel. Avec le xlrdmodule, je suis non seulement capable de lire correctement le fichier, mais je peux également accéder à de nombreuses parties différentes du fichier d'une manière que je ne pouvais pas auparavant.

J'ai pensé que ça pourrait t'aider.

ayaz
la source
7
Merci d'avoir signalé ce module. Fait intéressant, je suis allé le télécharger et j'ai remarqué que l'auteur n'était autre que @John_Machin qui est également le principal commentaire sur cette question.
Evan
11

La conversion de l'encodage du fichier source de UTF-16 en UTF-8 résout mon problème.

Comment convertir un fichier en utf-8 en 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
la source
8

Vous pouvez simplement insérer un générateur pour filtrer les valeurs nulles si vous voulez faire semblant qu'elles n'existent pas. Bien sûr, cela suppose que les octets nuls ne font pas vraiment partie de l'encodage et sont vraiment une sorte d'artefact ou de bogue erroné.

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
la source
2

Pourquoi fais-tu ça?

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

Les documents sont assez clairs sur le fait que vous devez le faire:

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

Le mode doit être "rb" pour lire.

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

Si csvfile est un objet fichier, il doit être ouvert avec l'indicateur «b» sur les plates-formes où cela fait une différence.

S.Lott
la source
@ AP257: "Ça n'aide pas"? Ça signifie quoi? Des messages d'erreur spécifiques?
S.Lott
1
@ S.Lott: Cela signifie qu'il obtient la même réponse qu'avant. La réalité est qu'il a affaire à un fichier caméléon ou métamorphe ... quand il le vide odou le regarde dans un éditeur de texte, il ressemble à un fichier CSV parfaitement normal. Cependant, quand il vide les premiers octets avec Python repr (), cela ressemble à un fichier Excel .XLS (qui a été renommé pour avoir une extension CSV).
John Machin
@John Machin: "un fichier Excel .XLS (qui a été renommé pour avoir une extension CSV" Cela a du sens qu'il ne peut pas être traité du tout.
S.Lott
1
@ S.Lott: Avec ce contenu, il est logique que le module csv ne puisse pas le traiter; cependant le module xlrd peut le traiter. Sensiblement, aucun module n'infère quoi que ce soit du nom du fichier d'entrée, si en effet l'entrée est un fichier avec un nom.
John Machin
1
@John Machin: "aucun module n'infère quoi que ce soit du nom du fichier d'entrée". Vrai. Mon cadre d'application dépend de ce fait. Nous ne croyons pas que le nom du fichier signifie quoi que ce soit, car les gens font des erreurs («mensonge»). Nous devons donc vérifier un tas d'alternatives jusqu'à ce que l'on clique.
S.Lott
2

apparemment c'est un fichier XLS et non un fichier CSV comme http://www.garykessler.net/library/file_sigs.html confirme

Xavier Combelle
la source
Pas nécessairement, mais oui, cela pourrait être une cause. J'ai eu cette erreur lorsque j'ai essayé d'analyser un fichier CSV qui a été enregistré par Excel à partir d'un fichier XLSX.
Cerin
Avec ce nombre magique, c'est la raison pour laquelle XLSX a un nombre magique différent
Xavier Combelle
2

Au lieu du lecteur csv, j'utilise un fichier de lecture et une fonction de fractionnement pour la chaîne:

lines = open(input_file,'rb') 

for line_all in lines:

    line=line_all.replace('\x00', '').split(";")
Nico le pinceau
la source
1

J'ai eu la même erreur. Enregistré le fichier en UTF-8 et cela a fonctionné.

mikaiscute
la source
1
Vous avez peut-être reçu le même message d'erreur, mais la cause en aurait été différente - vous l'avez probablement enregistré à l'origine sous le nom UTF-16 (ce que le Bloc-notes appelle "Unicode").
John Machin
1

Cela m'est arrivé lorsque j'ai créé un fichier CSV avec OpenOffice Calc. Cela ne s'est pas produit lorsque j'ai créé le fichier CSV dans mon éditeur de texte, même si je l'ai modifié plus tard avec Calc.

J'ai résolu mon problème en copiant-collant dans mon éditeur de texte les données de mon fichier créé par Calc dans un nouveau fichier créé par l'éditeur.

user1990371
la source
1

J'ai eu le même problème lors de l'ouverture d'un CSV produit à partir d'un webservice qui insérait des octets NULL dans des en-têtes vides. J'ai fait ce qui suit pour nettoyer le fichier:

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

Clause de non-responsabilité: sachez que cela écrase vos données d'origine. Assurez-vous d'en avoir une copie de sauvegarde. Tu étais prévenu!

Matthias Kuhn
la source
0

Pour tous ces haters du mode de fichier `` rU '': j'ai juste essayé d'ouvrir un fichier CSV à partir d'une machine Windows sur un Mac avec le mode de fichier `` rb '' et j'ai eu cette erreur du module csv:

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

L'ouverture du fichier en mode «rU» fonctionne correctement. J'adore le mode universel-nouvelle ligne - cela me permet d'économiser beaucoup de tracas.

Bill Gross
la source
0

J'ai rencontré cela lors de l'utilisation de scrapy et de la récupération d'un fichier csv compressé sans avoir un middleware correct pour décompresser le corps de la réponse avant de le remettre au csvreader. Par conséquent, le fichier n'était pas vraiment un fichier csv et a renvoyé l' line contains NULL byteerreur en conséquence.

Gesias
la source
0

Avez-vous essayé d'utiliser gzip.open?

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

J'essayais d'ouvrir un fichier qui avait été compressé mais qui avait l'extension «.csv» au lieu de «csv.gz». Cette erreur a continué à apparaître jusqu'à ce que j'utilise gzip.open

Munene iUwej Julius
la source
-1

Un cas est que - Si le fichier CSV contient des lignes vides, cette erreur peut apparaître. Vérifier la ligne est nécessaire avant de procéder à l'écriture ou à la lecture.

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

J'ai résolu mon problème en ajoutant cette vérification dans le code.

kirancodifier
la source