Błąd CSV w Pythonie: wiersz zawiera bajt NULL

102

Pracuję z kilkoma plikami CSV z następującym kodem:

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

Jeden plik zgłasza ten błąd:

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

Co mogę zrobić? Wydaje się, że Google sugeruje, że może to być plik Excela, który został nieprawidłowo zapisany jako .csv. Czy jest jakiś sposób na obejście tego problemu w Pythonie?

== AKTUALIZACJA ==

Po komentarzu @ JohnMachin poniżej, próbowałem dodać te wiersze do mojego skryptu:

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

A oto wynik, który otrzymałem:

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

Więc plik rzeczywiście zawiera bajty NUL.

AP257
źródło
Jak wygląda od -cpierwsza linijka?
Ignacio Vazquez-Abrams
jakie zapytanie mam uruchomić, coś w rodzaju cat my.csv | od -c | więcej ? w ten sposób otrzymuję: 0000000 D epartment F amil
AP257
Jak generowany jest plik CSV? W programie Excel możesz spróbować dialektu. W przeciwnym razie spójrz na powiedz: stackoverflow.com/questions/2753022/ ...
dr jimbob
Dzięki. To nie jest mój plik CSV i niestety nie mam uprawnień, aby to zmienić. Myślę, że został utworzony jako Excel i zapisany jako CSV (boo). Dialekt brzmi jak dobry pomysł - spróbuję!
AP257,
Jeśli faktycznie został zapisany jako CSV, powinien działać. Jedną z rzeczy, które czasami znajduję, są pliki TSV (oddzielone tabulatorami) udające CSV, więc możesz spróbować ustawić separator „\ t”. Jeśli został zapisany jako plik Excela, a rozszerzenie zostało zmienione na CSV, żaden dialekt nie zadziała. Myślę, że jedyną opcją w takim przypadku byłoby użycie programu Excel do zapisywania kopii w odpowiednim pliku CSV.
Thomas K,

Odpowiedzi:

104

Jak mówi @ S.Lott, powinieneś otwierać swoje pliki w trybie 'rb', a nie 'rU'. Jednak może to NIE być przyczyną obecnego problemu. O ile wiem, użycie trybu „rU” mogłoby zepsuć cię, jeśli są osadzone\r , ale nie spowodowałoby żadnych innych dramatów. Zauważyłem również, że masz kilka plików (wszystkie otwarte za pomocą 'rU' ??), ale tylko jeden powoduje problem.

Jeśli moduł csv mówi, że masz w pliku bajt „NULL” (głupia wiadomość, powinna mieć wartość „NUL”), musisz sprawdzić, co jest w pliku. Sugerowałbym, abyś to zrobił, nawet jeśli użycie „rb” spowoduje, że problem zniknie.

repr()jest (lub chce być) twoim przyjacielem od debugowania. Pokaże jednoznacznie, co masz, w sposób niezależny od platformy (co jest pomocne dla pomocników, którzy nie wiedzą, co odjest lub co robi). Zrób to:

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

i ostrożnie skopiuj / wklej (nie wpisuj ponownie) wynik do edycji twojego pytania (nie do komentarza).

Zauważ również, że jeśli plik jest naprawdę podejrzany, np. Nie ma \ r lub \ n w rozsądnej odległości od początku pliku, numer linii zgłaszany przez reader.line_numbędzie (nieprzydatny) 1. Znajdź, gdzie \x00jest pierwsza (jeśli istnieje), wykonując

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

i upewnij się, że co najmniej tyle bajtów zrzuciłeś za pomocą polecenia repr lub od.

Co data.count('\x00')ci powie? Jeśli jest ich wiele, możesz chcieć zrobić coś takiego

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

abyś mógł zobaczyć bajty NUL w kontekście.

Jeśli widzisz \x00na wyjściu (lub \0w swoim od -cwyniku), to na pewno masz bajt (y) NUL w pliku i będziesz musiał zrobić coś takiego:

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

Przy okazji, czy obejrzałeś plik (w tym kilka ostatnich wierszy) za pomocą edytora tekstu? Czy faktycznie wygląda jak rozsądny plik CSV, podobnie jak inne pliki (bez wyjątku „bajtu zerowego”)?

John Machin
źródło
Bardzo dziękuję za tę bardzo szczegółową pomoc. Plik zawiera dużo \ x00 znaków (patrz edycja pytania) - to dziwne, ponieważ w edytorze tekstu wygląda jak całkiem rozsądny plik CSV.
AP257
1
@ AP257: '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1to "podpis" oznaczający plik dokumentu złożonego OLE2 - np . Plik Excel 97-2003 .XLS . Uważam, że „w edytorze tekstu wygląda to jak całkiem rozsądny plik CSV” za całkowicie niewiarygodne . Musiałeś patrzeć na inny plik, prawidłowy plik CSV, w innym folderze, na innym komputerze lub w innym czasie. Zauważ, że oddane wyjściowe nie pochodzą z pliku XLS.
John Machin
8
@ AP257: Czy jest jakiś szczególny powód, dla którego nie zaakceptowałeś tej odpowiedzi?
John Machin,
Działa, ale powinno być możliwe i przyjemne w locie z obiektem podobnym do pliku, który filtruje CSV i może być przekazywany csv.readerbezpośrednio.
gerrit
1
Nie powinno fo.write(data.replace('\x00', ''))być fo.write(data.replace(b'\x00', b''))? Python 3.6 tutaj ...
Boern
23
data_initial = open("staff.csv", "rb")
data = csv.reader((line.replace('\0','') for line in data_initial), delimiter=",")

To działa dla mnie.

podwójnie
źródło
Rozwiązany dla mojego przypadku, null to wartości „\ 0”. Dzięki.
Joab Mendes,
19

Odczytanie go jako UTF-16 też było moim problemem.

Oto mój kod, który zakończył się działaniem:

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

Gdzie lokalizacja to katalog twojego pliku csv.

Użytkownik
źródło
13

Ja też wpadłem na ten problem. Korzystając z csvmodułu Python , próbowałem odczytać plik XLS utworzony w MS Excel i napotkałem wyświetlany NULL bytebłąd. Rozejrzałem się i znalazłem moduł xlrd Python do odczytu i formatowania danych z plików arkusza kalkulacyjnego MS Excel. Dzięki temu xlrdmodułowi mogę nie tylko poprawnie odczytać plik, ale mogę również uzyskać dostęp do wielu różnych części pliku w sposób, w jaki wcześniej nie mogłem.

Pomyślałem, że to może ci pomóc.

ayaz
źródło
7
Dzięki za wskazanie tego modułu. Co ciekawe, poszedłem go pobrać i zauważyłem, że autorem był nikt inny jak @John_Machin, który jest również najpopularniejszym komentarzem do tego pytania.
Evan,
11

Konwersja kodowania pliku źródłowego z UTF-16 na UTF-8 rozwiązuje mój problem.

Jak przekonwertować plik do utf-8 w Pythonie?

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
źródło
8

Możesz po prostu wbudować generator, aby odfiltrować wartości null, jeśli chcesz udawać, że nie istnieją. Oczywiście zakłada się, że bajty zerowe nie są tak naprawdę częścią kodowania i naprawdę są jakimś błędnym artefaktem lub błędem.

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
źródło
2

Dlaczego to robisz?

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

Dokumenty są dość jasne, że musisz to zrobić:

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

Tryb odczytu musi mieć wartość „rb”.

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

Jeśli csvfile jest obiektem pliku, musi być otwierany z flagą „b” na platformach, na których ma to znaczenie.

S.Lott
źródło
@ AP257: „To nie pomaga”? Znaczy co? Jakieś konkretne komunikaty o błędach?
S.Lott
1
@ S.Lott: Oznacza, że ​​otrzymuje taką samą odpowiedź jak poprzednio. W rzeczywistości ma do czynienia z plikiem kameleona lub zmiennokształtnego ... kiedy zrzuca go odlub patrzy na niego w edytorze tekstu, wygląda jak idealnie normalny plik CSV. Jednak kiedy zrzuca pierwsze kilka bajtów za pomocą Pythona repr (), tworzy jak plik Excel .XLS (jego nazwa została zmieniona, aby mieć rozszerzenie CSV).
John Machin
@John Machin: „plik Excel .XLS (którego nazwa została zmieniona i ma rozszerzenie CSV”). Ma sens, że nie może być w ogóle przetworzony.
S.Lott
1
@ S.Lott: Z tą zawartością ma sens, że moduł csv nie może jej przetworzyć; jednakże moduł xlrd może go przetworzyć. Rozsądnie, żaden moduł nie wywnioskuje niczego z nazwy pliku wejściowego, jeśli rzeczywiście wejście jest plikiem o nazwie.
John Machin,
1
@John Machin: "Żaden moduł nie wywnioskuje niczego z nazwy pliku wejściowego". Prawdziwe. Moje ramy aplikacji zależą od tego faktu. Nie ufamy, że nazwa pliku cokolwiek znaczy, ponieważ ludzie popełniają błędy („kłamią”). Musimy więc sprawdzić kilka alternatyw, aż do jednego kliknięcia.
S.Lott,
2

najwyraźniej jest to plik XLS, a nie plik CSV, jak http://www.garykessler.net/library/file_sigs.html potwierdź

Xavier Combelle
źródło
Niekoniecznie, ale tak, to może być przyczyna. Wystąpił ten błąd, gdy próbowałem przeanalizować plik CSV, który został zapisany przez program Excel z pliku XLSX.
Cerin
Dzięki tej magicznej liczbie jest to przyczyna, dla której XLSX ma inny magiczny numer
Xavier Combelle
2

Zamiast czytnika csv używam funkcji read file i split dla stringów:

lines = open(input_file,'rb') 

for line_all in lines:

    line=line_all.replace('\x00', '').split(";")
Nico The Brush
źródło
1

Mam ten sam błąd. Zapisałem plik w UTF-8 i zadziałało.

mikaiscute
źródło
1
Być może otrzymałeś ten sam komunikat o błędzie, ale przyczyna byłaby inna - prawdopodobnie zapisałeś go pierwotnie jako UTF-16 (co Notatnik nazywa „Unicode”).
John Machin
1

Zdarzyło mi się to, gdy utworzyłem plik CSV za pomocą OpenOffice Calc. Tak się nie stało, gdy utworzyłem plik CSV w moim edytorze tekstu, nawet jeśli później edytowałem go w programie Calc.

Rozwiązałem problem, kopiując i wklejając w edytorze tekstu dane z mojego pliku utworzonego w programie Calc do nowego pliku utworzonego przez edytor.

user1990371
źródło
1

Miałem ten sam problem podczas otwierania pliku CSV utworzonego z usługi sieciowej, która wstawiała bajty NULL do pustych nagłówków. Wykonałem następujące czynności, aby wyczyścić plik:

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

Zastrzeżenie: pamiętaj, że spowoduje to nadpisanie oryginalnych danych. Upewnij się, że masz jego kopię zapasową. Zostałeś ostrzeżony!

Matthias Kuhn
źródło
0

Dla wszystkich tych, którzy nie znoszą trybu plików `` rU '': właśnie próbowałem otworzyć plik CSV z komputera z systemem Windows na komputerze Mac z trybem pliku `` rb '' i otrzymałem ten błąd z modułu csv:

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

Otwarcie pliku w trybie „rU” działa poprawnie. Uwielbiam tryb Universal-Newline - oszczędza mi to wiele kłopotów.

Bill Gross
źródło
0

Napotkałem to podczas używania scrapy i pobierania skompresowanego pliku csv bez odpowiedniego oprogramowania pośredniczącego do rozpakowania treści odpowiedzi przed przekazaniem jej do csvreadera. W związku z tym plik nie był w rzeczywistości plikiem csv i odpowiednio wyrzucił line contains NULL bytebłąd.

Gesias
źródło
0

Czy próbowałeś użyć gzip.open?

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

Próbowałem otworzyć plik, który został skompresowany, ale miał rozszerzenie „.csv” zamiast „csv.gz”. Ten błąd pojawiał się, dopóki nie użyłem gzip.open

Munene iUwej Julius
źródło
-1

Jeden przypadek jest taki - jeśli plik CSV zawiera puste wiersze, ten błąd może się pojawić. Przed przystąpieniem do pisania lub czytania należy sprawdzić wiersz.

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

Rozwiązałem problem, dodając to sprawdzenie w kodzie.

kirancodify
źródło