Odczytywanie pliku CSV UTF8 w Pythonie

93

Próbuję odczytać plik CSV ze znakami akcentowanymi w języku Python (tylko znaki francuskie i / lub hiszpańskie). W oparciu o dokumentację Pythona 2.5 dla csvreadera ( http://docs.python.org/library/csv.html ) wymyśliłem następujący kod do odczytu pliku CSV, ponieważ csvreader obsługuje tylko ASCII.

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    for line in unicode_csv_data:
        yield line.encode('utf-8')

filename = 'output.csv'
reader = unicode_csv_reader(open(filename))
try:
    products = []
    for field1, field2, field3 in reader:
        ...

Poniżej znajduje się fragment pliku CSV, który próbuję przeczytać:

0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert
...

Mimo że próbuję zakodować / zdekodować do UTF-8, nadal otrzymuję następujący wyjątek:

Traceback (most recent call last):
  File ".\Test.py", line 53, in <module>
    for field1, field2, field3 in reader:
  File ".\Test.py", line 40, in unicode_csv_reader
    for row in csv_reader:
  File ".\Test.py", line 46, in utf_8_encoder
    yield line.encode('utf-8', 'ignore')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 68: ordinal not in range(128)

Jak to naprawić?

Jaskółka oknówka
źródło
Martin, jeśli jesteś w pobliżu, czy rozważyłbyś zmianę zaakceptowanej odpowiedzi z jedynej odpowiedzi Martelli w Pythonie 2.
Antti Haapala

Odpowiedzi:

113

.encodeMetoda zostanie zastosowana na ciąg Unicode dokonania ciąg bajtów; ale zamiast tego nazywasz to ciągiem bajtów ... w niewłaściwy sposób! Przyjrzyj się codecsmodułowi w standardowej bibliotece, a codecs.openzwłaszcza lepszym ogólnym rozwiązaniom do odczytu plików tekstowych zakodowanych w formacie UTF-8. Jednak csvw szczególności w przypadku modułu musisz przekazać dane utf-8 i to właśnie otrzymujesz, więc twój kod może być znacznie prostszy:

import csv

def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
    csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
    for row in csv_reader:
        yield [unicode(cell, 'utf-8') for cell in row]

filename = 'da.csv'
reader = unicode_csv_reader(open(filename))
for field1, field2, field3 in reader:
  print field1, field2, field3 

PS: jeśli okaże się, że twoje dane wejściowe NIE są w utf-8, ale np. W ISO-8859-1, to potrzebujesz "transkodowania" (jeśli chcesz używać utf-8 na csvpoziomie modułu) , formularza line.decode('whateverweirdcodec').encode('utf-8')- ale prawdopodobnie możesz po prostu użyć nazwy istniejącego kodowania w yieldlinii w moim kodzie powyżej, zamiast 'utf-8', jak csvto będzie dobrze z bajtami zakodowanymi w ISO-8859- *.

Alex Martelli
źródło
4
Czy to oznacza, że ​​przykład w dokumentach Pythona (gdzie OP jest kopiowany i wklejany z) jest nieprawidłowy? Jaki jest sens dodatkowego kroku kodowania, który robi, jeśli zepsuje się, gdy nadasz mu plik CSV w formacie Unicode?
Anentropic
84

Python 2.X

Istnieje biblioteka unicode-csv, która powinna rozwiązać twoje problemy, z dodatkową korzyścią polegającą na tym, że nie przechodzisz do pisania nowego kodu związanego z CSV.

Oto przykład z ich pliku readme:

>>> import unicodecsv
>>> from cStringIO import StringIO
>>> f = StringIO()
>>> w = unicodecsv.writer(f, encoding='utf-8')
>>> w.writerow((u'é', u'ñ'))
>>> f.seek(0)
>>> r = unicodecsv.reader(f, encoding='utf-8')
>>> row = r.next()
>>> print row[0], row[1]
é ñ

Python 3.X

W Pythonie 3 jest to obsługiwane po wyjęciu z pudełka przez wbudowany csvmoduł. Zobacz ten przykład:

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
jb.
źródło
10

Jeśli chcesz czytać plik CSV z kodowaniem utf-8, minimalistyczne podejście, które polecam, to użycie czegoś takiego:

with open(file_name, encoding="utf8") as csv_file:

Dzięki temu stwierdzeniu możesz później użyć czytnika CSV do pracy.

Nick Cuevas
źródło
2
Czy to możliwe, że to tylko Python 3? To nie dla mnie, w Pythonie 2. To nie akceptuje encodingwopen
Zvika
@Zvika tak, w Pythonie 3 to rozwiązanie działa:open('file.csv', 'r', encoding="ISO8859")
luca76
Dodałbym też open (nazwa_pliku, „rt”, encoding = 'utf-8'), czyli otwórz plik w trybie „odczytu tekstu”
Jimmy Lee Jones
3

Sprawdź również odpowiedź w tym poście: https://stackoverflow.com/a/9347871/1338557

Sugeruje użycie biblioteki o nazwie ucsv.py. Krótki i prosty zamiennik CSV napisany w celu rozwiązania problemu z kodowaniem (utf-8) dla Pythona 2.7. Zapewnia również obsługę csv.DictReader

Edycja : Dodawanie przykładowego kodu, którego użyłem:

import ucsv as csv

#Read CSV file containing the right tags to produce
fileObj = open('awol_title_strings.csv', 'rb')
dictReader = csv.DictReader(fileObj, fieldnames = ['titles', 'tags'], delimiter = ',', quotechar = '"')
#Build a dictionary from the CSV file-> {<string>:<tags to produce>}
titleStringsDict = dict()
for row in dictReader:
    titleStringsDict.update({unicode(row['titles']):unicode(row['tags'])})
Atripavan
źródło
w swojej odpowiedzi powinieneś podać szczegóły tego linku, na wypadek gdyby link się zepsuł \
Yaje
# Downvoter - Nie wiem, dlaczego pomyślałeś, że to nie ma sensu. Biblioteka ucsv działała dobrze dla mnie. Pomógł rozwiązać błąd unicde, z którym borykałem się od 2 dni. Jeśli szukałeś jakiegoś przykładowego kodu, tutaj jest on w edit @ Yaje- podałem kilka szczegółów; również przykładowy kod. Poprawiliśmy również link, który wskazywał wcześniej na inny post.
Atripavan
Czy jest jakiś szczególny powód, dla którego otwierasz plik tekstowy jako plik binarny? „rb” służy do otwierania plików binarnych.
Codeguy007
2

Używanie tego, codecs.openco sugerował Alex Martelli, okazało się przydatne dla mnie.

import codecs

delimiter = ';'
reader = codecs.open("your_filename.csv", 'r', encoding='utf-8')
for line in reader:
    row = line.split(delimiter)
    # do something with your row ...
user1154664
źródło
3
Nie działałoby to ze wszystkimi plikami CSV, poniżej znajduje się prawidłowy wiersz csv: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.
Importujesz csvmoduł, ale go nie używasz.
Christophe Roussy
1

Link do strony pomocy jest taki sam dla Pythona 2.6 iz tego co wiem, nie było żadnych zmian w module csv od 2.5 (poza poprawkami błędów). Oto kod, który po prostu działa bez kodowania / dekodowania (plik da.csv zawiera te same dane, co dane zmienne ). Zakładam, że twój plik powinien być odczytany poprawnie bez żadnych konwersji.

test.py:

## -*- coding: utf-8 -*-
#
# NOTE: this first line is important for the version b) read from a string(unicode) variable
#

import csv

data = \
"""0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert"""

# a) read from a file
print 'reading from a file:'
for (f1, f2, f3) in csv.reader(open('da.csv'), dialect=csv.excel):
    print (f1, f2, f3)

# b) read from a string(unicode) variable
print 'reading from a list of strings:'
reader = csv.reader(data.split('\n'), dialect=csv.excel)
for (f1, f2, f3) in reader:
    print (f1, f2, f3)

da.csv:

0665000FS10120684,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Bleu
0665000FS10120689,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Gris
0665000FS10120687,SD1200IS,Appareil photo numérique PowerShot de 10 Mpx de Canon avec trépied (SD1200IS) - Vert
awangarda
źródło
Zastanawiam się, w której wersji Pythona to zadziała? Otrzymuję błędy zarówno z 2.7, jak i 3.5. „ValueError: za mało wartości do rozpakowania (oczekiwano 3, otrzymano 1)”
eis
@eis: Mogę sobie wyobrazić, że przecinek w Twoim systemie nie jest domyślnym separatorem. Spróbuj dodać delimiter=','zamiast dialect=csv.excel.
van
1

Warto zauważyć, że jeśli nic nie działało dla ciebie, być może zapomniałeś uciec ze swojej ścieżki.
Na przykład ten kod:

f = open("C:\Some\Path\To\file.csv")

Spowodowałoby błąd:

SyntaxError: (błąd Unicode) Kodek „unicodeescape” nie może zdekodować bajtów na pozycji 2-3: obcięty \ UXXXXXXXX ucieczka

Aby naprawić, po prostu wykonaj:

f = open("C:\\Some\\Path\\To\\file.csv")
OfirD
źródło
0

Patrząc na Latin-1tabelę Unicode , widzę kod znaku 00E9ŁACIŃSKA MAŁA LITERA E Z OSTRO ”. To jest znak akcentowany w przykładowych danych. Prosty test Pythonpokazuje, że UTF-8kodowanie dla tego znaku różni się od (prawie UTF-16) kodowania Unicode .

>>> u'\u00e9'
u'\xe9'
>>> u'\u00e9'.encode('utf-8')
'\xc3\xa9'
>>> 

Proponuję spróbować encode("UTF-8")danych Unicode przed wywołaniem oferty specjalnej unicode_csv_reader(). Samo odczytanie danych z pliku może ukryć kodowanie, więc sprawdź rzeczywiste wartości znaków.

gimel
źródło
0

Miałem ten sam problem na innym serwerze, ale zdałem sobie sprawę, że ustawienia regionalne są pomieszane.

export LC_ALL="en_US.UTF-8"

naprawiono problem

Piotr Pęczek
źródło