Leggere un file CSV UTF8 con Python


93

Sto cercando di leggere un file CSV con caratteri accentati con Python (solo caratteri francesi e / o spagnoli). Sulla base della documentazione di Python 2.5 per csvreader ( http://docs.python.org/library/csv.html ), ho trovato il seguente codice per leggere il file CSV poiché csvreader supporta solo 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:
        ...

Di seguito è riportato un estratto del file CSV che sto cercando di leggere:

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

Anche se provo a codificare / decodificare in UTF-8, ricevo ancora la seguente eccezione:

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)

Come lo risolvo?


Martin, se sei in giro, prenderesti in considerazione di cambiare la risposta accettata dall'unica risposta di Martelli Python 2.
Antti Haapala

Risposte:


113

Il .encodemetodo viene applicato a una stringa Unicode per creare una stringa di byte; ma invece lo stai chiamando su una stringa di byte ... nel modo sbagliato! Guarda il codecsmodulo nella libreria standard e codecs.openin particolare per migliori soluzioni generali per la lettura di file di testo codificati UTF-8. Tuttavia, per il csvmodulo in particolare, devi passare i dati utf-8, ed è quello che stai già ottenendo, quindi il tuo codice può essere molto più semplice:

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: se risulta che i tuoi dati di input NON sono in utf-8, ma ad esempio in ISO-8859-1, allora hai bisogno di una "transcodifica" (se ti piace usare utf-8 a csvlivello di modulo) , della forma line.decode('whateverweirdcodec').encode('utf-8')- ma probabilmente puoi semplicemente usare il nome della tua codifica esistente nella yieldriga nel mio codice sopra, invece di 'utf-8', come in csvrealtà andrà bene con ISO-8859- * codificato bytestrings.


4
Questo significa che l'esempio nella documentazione di Python (da cui OP copia e incolla) è sbagliato? Qual è lo scopo del passaggio di codifica extra che fa se si interrompe quando gli dai un csv unicode?
Anentropic


80

Python 2.X

Esiste una libreria unicode-csv che dovrebbe risolvere i tuoi problemi, con l'ulteriore vantaggio di non navigare per scrivere alcun nuovo codice relativo a csv.

Ecco un esempio dal loro file 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

In python 3 questo è supportato immediatamente dal csvmodulo integrato. Guarda questo esempio:

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

8

Se vuoi leggere un file CSV con codifica utf-8, un approccio minimalista che ti consiglio è di usare qualcosa del genere:

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

Con questa dichiarazione, puoi utilizzare in seguito un lettore CSV con cui lavorare.


2
È possibile che questo sia solo Python 3? Per me fallisce, in Python 2. Non accetta encodinginopen
Zvika

@Zvika sì, in python 3 questa soluzione funziona:open('file.csv', 'r', encoding="ISO8859")
luca76

Aggiungerei anche open (nome_file, "rt", encoding = 'utf-8'), ovvero file aperto in modalità "leggi testo"
Jimmy Lee Jones

3

Controlla anche la risposta in questo post: https://stackoverflow.com/a/9347871/1338557

Suggerisce l'uso della libreria chiamata ucsv.py. Breve e semplice sostituzione per CSV scritto per risolvere il problema di codifica (utf-8) per Python 2.7. Fornisce anche il supporto per csv.DictReader

Modifica : aggiunta del codice di esempio che ho usato:

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

dovresti inserire alcuni dettagli di quel collegamento nella tua risposta, nel caso in cui il collegamento si
interrompa

# Downvoter- Non sono sicuro del motivo per cui hai pensato che fosse inutile. La libreria ucsv ha funzionato perfettamente per me. Mi ha aiutato a risolvere l'errore Unicde con cui ho lottato da 2 giorni. Se stavi cercando un codice di esempio, eccolo nella modifica @ Yaje- Ho fornito alcuni dettagli; anche il codice di esempio. E ho corretto anche il collegamento, che prima puntava a qualche altro post.
Atripavan

Qualche motivo particolare per aprire un file di testo come binario? "rb" serve per aprire file binari.
Codeguy007

2

L'utilizzo di codecs.openquanto suggerito da Alex Martelli si è rivelato utile.

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
Non funzionerebbe con tutti i CSV, di seguito è riportata una riga CSV valida: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.

Importi il csvmodulo ma non lo usi.
Christophe Roussy

1

Il collegamento alla pagina della guida è lo stesso per python 2.6 e per quanto ne so non ci sono state modifiche nel modulo csv dalla 2.5 (a parte le correzioni di bug). Ecco il codice che funziona senza alcuna codifica / decodifica (il file da.csv contiene gli stessi dati dei dati variabili ). Presumo che il tuo file debba essere letto correttamente senza alcuna conversione.

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

Mi chiedo in quale versione di Python funzionerebbe? Ottengo errori sia con 2.7 che con 3.5. "ValueError: valori insufficienti per decomprimere (previsto 3, ottenuto 1)"
eis

@eis: posso immaginare che sul tuo sistema la virgola non sia un delimitatore predefinito. Prova ad aggiungere delimiter=','invece di dialect=csv.excel.
furgone

1

Vale la pena notare che se niente ha funzionato per te, potresti aver dimenticato di sfuggire al tuo percorso.
Ad esempio, questo codice:

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

Risulterebbe un errore:

SyntaxError: (errore unicode) il codec 'unicodeescape' non può decodificare i byte nella posizione 2-3: troncato \ UXXXXXXXX escape

Per risolvere, fai semplicemente:

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

0

Guardando la Latin-1tabella unicode , vedo il codice carattere 00E9" LATIN SMALL LETTER E WITH ACUTE ". Questo è il carattere accentato nei dati di esempio. Un semplice test Pythonmostra che la UTF-8codifica per questo carattere è diversa dalla (quasi UTF-16) codifica unicode .

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

Ti suggerisco di provare encode("UTF-8")i dati unicode prima di chiamare lo speciale unicode_csv_reader(). La semplice lettura dei dati da un file potrebbe nascondere la codifica, quindi controlla i valori effettivi dei caratteri.


0

Ha avuto lo stesso problema su un altro server, ma si è reso conto che le impostazioni locali sono incasinate.

export LC_ALL="en_US.UTF-8"

risolto il problema

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.