99 votes

Lecture d'un fichier CSV UTF8 avec Python

Je suis en train d'essayer de lire un fichier CSV avec des caractères accentués en Python (uniquement des caractères français et/ou espagnols). Basé sur la documentation Python 2.5 pour le csvreader (http://docs.python.org/library/csv.html), j'ai écrit le code suivant pour lire le fichier CSV puisque le csvreader ne supporte que l'ASCII.

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    # csv.py ne gère pas l'Unicode ; encoder temporairement en UTF-8 :
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # décodez l'UTF-8 en Unicode, cellule par cellule :
        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:
        ...

Voici un extrait du fichier CSV que j'essaie de lire :

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

Même si j'essaye d'encoder/décoder en UTF-8, je reçois toujours l'exception suivante :

Traceback (most recent call last):
  File ".\Test.py", line 53, in 
    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 ne peut pas décoder le caractère 0xc3 à la position 68: ordinal non compris dans la plage(128)

Comment puis-je résoudre ce problème ?

0 votes

Martin, si tu es dans les parages, pourrais-tu envisager de changer la réponse acceptée de la réponse Python 2 de Martelli.

117voto

Alex Martelli Points 330805

La méthode .encode est appliquée à une chaîne Unicode pour en faire une chaîne d'octets; mais tu l'appelles sur une chaîne d'octets à la place... à l'envers! Regarde le module codecs dans la bibliothèque standard et en particulier codecs.open pour de meilleures solutions générales pour lire des fichiers texte encodés en UTF-8. Cependant, pour le module csv en particulier, tu dois passer des données utf-8, ce que tu obtiens déjà, donc ton code peut être beaucoup plus simple:

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 : Si tu constates que tes données d'entrée NE sont PAS en utf-8, mais par exemple en ISO-8859-1, alors tu as besoin d'un "transcodage" (si tu tiens à utiliser utf-8 au niveau du module csv), sous la forme line.decode('whateverweirdcodec').encode('utf-8') -- mais tu peux probablement juste utiliser le nom de ton codage existant dans la ligne yield de mon code ci-dessus, au lieu de 'utf-8', car csv fonctionne très bien avec des chaînes d'octets encodées en ISO-8859-*.

4 votes

Cela signifie-t-il que l'exemple dans la documentation Python (d'où OP a copié et collé) est incorrect ? Quel est l'intérêt de l'étape d'encodage supplémentaire qu'il effectue s'il se casse lorsque vous lui donnez un CSV unicode ?

0 votes

93voto

jb. Points 4932

Python 2.X

Il existe une bibliothèque unicode-csv qui devrait résoudre vos problèmes, avec l'avantage supplémentaire de ne pas avoir à écrire de nouveau code lié au csv.

Voici un exemple tiré de leur 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

En python 3, cela est pris en charge directement par le module csv intégré. Voici un exemple:

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

3 votes

encoding='utf-8-sig' aide si votre fichier CSV a un préfixe BOM U+FEFF. En ouvrant le fichier avec cet encodage, le BOM sera automatiquement supprimé. Sinon, cela perturbe csv en pensant que le premier nom de champ commence par le caractère BOM et échoue à supprimer les guillemets, et ainsi reader.fieldnames[0] peut être '\ufeff"Date"' au lieu de 'Date'.

2voto

user1154664 Points 618

Utiliser codecs.open comme l'a suggéré Alex Martelli s'est avéré utile pour moi.

import codecs

delimiter = ';'
reader = codecs.open("votre_nom_de_fichier.csv", 'r', encoding='utf-8')
for line in reader:
    row = line.split(delimiter)
    # faire quelque chose avec votre ligne ...

3 votes

Il ne fonctionnerait pas avec tous les CSV, ce qui suit est une ligne de CSV valide : "Foo Bar; Baz"; 231; 313; ";;;"; 1;

0 votes

Vous importez le module csv mais ne l'utilisez pas.

1voto

van Points 18052

Le lien vers la page d'aide est le même pour python 2.6 et autant que je sache, il n'y a eu aucun changement dans le module csv depuis la version 2.5 (à part les correctifs de bogues). Voici le code qui fonctionne sans encodage/décodage (le fichier da.csv contient les mêmes données que la variable data). Je suppose que votre fichier devrait être lu correctement sans conversions.

test.py:

## -*- coding: utf-8 -*-
#
# NOTE: cette première ligne est importante pour la version b) lire à partir d'une variable de chaîne (unicode)
#

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) lire à partir d'un fichier
print 'lecture à partir d'un fichier:'
for (f1, f2, f3) in csv.reader(open('da.csv'), dialect=csv.excel):
    print (f1, f2, f3)

# b) lire à partir d'une variable de chaîne (unicode)
print 'lecture à partir d'une liste de chaînes:'
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

0 votes

Je me demande dans quelle version de Python cela fonctionnerait-il? J'obtiens des erreurs avec les versions 2.7 et 3.5. "ValueError: not enough values to unpack (expected 3, got 1)"

0 votes

@eis : Je peux imaginer que sur votre système, la virgule n'est pas un délimiteur par défaut. Essayez d'ajouter delimiter=',' au lieu de dialect=csv.excel.

0voto

gimel Points 30150

En regardant le tableau unicode Latin-1, je vois le code de caractère 00E9 "LETTRE MINUSCULE E AVEC ACCENT AIGU". C'est le caractère accentué dans vos données d'exemple. Un test simple en Python montre que l'encodage UTF-8 de ce caractère est différent de l'encodage unicode (presque UTF-16).

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

Je vous suggère d'essayer d'encoder("UTF-8") les données unicode avant d'appeler la fonction spéciale unicode_csv_reader(). Simplement lire les données à partir d'un fichier peut masquer l'encodage, donc vérifiez les valeurs de caractère réelles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X