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ć?


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


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)

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.


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

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

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

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

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.


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.