99 votes

Lecture d'un fichier CSV UTF8 avec Python

J'essaie de lire un fichier CSV avec des caractères accentués avec Python (uniquement des caractères français et/ou espagnols). D'après la documentation de Python 2.5 pour le csvreader ( http://docs.python.org/library/csv.html ), j'ai trouvé le code suivant pour lire le fichier CSV puisque le lecteur de CSV ne supporte que l'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:
        ...

Vous trouverez ci-dessous 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'essaie de coder/décoder en UTF-8, j'obtiens toujours l'exception suivante :

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)

Comment puis-je réparer cela ?

0 votes

Martin, si vous êtes dans le coin, pourriez-vous envisager de changer la réponse acceptée par rapport à la réponse de Martelli, qui ne concerne que Python 2.

117voto

Alex Martelli Points 330805

Le site .encode est appliquée à une chaîne Unicode pour créer une chaîne d'octets ; mais vous l'appelez sur une chaîne d'octets à la place... dans le mauvais sens ! Regardez la méthode codecs dans la bibliothèque standard et codecs.open en particulier pour de meilleures solutions générales pour la lecture de fichiers texte encodés en UTF-8. Cependant, pour le csv en particulier, vous devez transmettre des données utf-8, et c'est ce que vous obtenez déjà, votre code peut donc ê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 : s'il s'avère que vos données d'entrée ne sont PAS en utf-8, mais par exemple en ISO-8859-1, alors vous avez besoin d'un "transcodage" (si vous tenez à utiliser l'utf-8 à l'origine). csv au niveau du module), de la forme line.decode('whateverweirdcodec').encode('utf-8') -- mais vous pouvez probablement simplement utiliser le nom de votre encodage existant dans le fichier yield dans mon code ci-dessus, au lieu de 'utf-8' comme csv est en fait très bien avec les bytestrings encodés en ISO-8859-*.

4 votes

Cela signifie-t-il que l'exemple dans la documentation python (d'où OP a fait un copier-coller) est faux ? Quel est l'intérêt de l'étape d'encodage supplémentaire qu'il effectue s'il se casse quand on lui donne un csv unicode ?

0 votes

93voto

jb. Points 4932

Python 2.X

Il existe un unicode-csv qui devrait résoudre vos problèmes, avec l'avantage supplémentaire de ne pas avoir à écrire de nouveau code relatif 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

Dans python 3, cela est pris en charge par le module intégré csv module. Voir cet 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 de nomenclature U+FEFF. L'ouverture du fichier avec cet encodage supprimera automatiquement la nomenclature. Sinon, il y a confusion csv en pensant que le premier nom de champ commence par le caractère BOM et qu'il ne parvient pas à supprimer les guillemets, et donc que reader.fieldnames[0] peut être '\ufeff"Date"' au lieu de 'Date' .

2voto

user1154664 Points 618

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

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 votes

Cela ne fonctionne pas avec tous les CSV, ce qui suit est une ligne csv valide : "Foo Bar ; Baz" ; 231 ; 313 ; ";;; ;"; 1 ;

0 votes

Vous importez le 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 pour autant que je sache il n'y a pas eu de changement dans le module csv depuis 2.5 (à part des corrections de bugs). Voici le code qui fonctionne simplement sans aucun encodage/décodage (le fichier da.csv contient les mêmes données que la variable données ). Je suppose que votre fichier devrait être lu correctement sans aucune conversion.

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

0 votes

Je me demande dans quelle version de Python cela fonctionnerait ? J'obtiens des erreurs avec les versions 2.7 et 3.5. "ValueError : pas assez de valeurs à décompresser (attendu 3, obtenu 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 Latin-1 table unicode je vois le code du caractère 00E9 " LETTRE MINUSCULE LATINE E AVEC ACCENT AIGU ". Il s'agit du caractère accentué dans vos données d'exemple. Un test simple dans Python montre que UTF-8 L'encodage de ce caractère est différent de celui de l'unicode (presque tous les caractères de l'unicode). UTF-16 ).

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

Je vous suggère d'essayer encode("UTF-8") les données unicode avant d'appeler la fonction spéciale unicode_csv_reader() . La simple lecture des données d'un fichier peut masquer l'encodage, il faut donc vérifier les valeurs réelles des caractères.

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