Lire un fichier CSV UTF8 avec Python

93

J'essaie de lire un fichier CSV avec des caractères accentués avec 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 proposé le code suivant pour lire le fichier CSV car le csvreader ne prend en charge 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:
        ...

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'essaie d'encoder / 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 ça?

Martin
la source
Martin, si vous êtes dans le coin, envisageriez-vous de changer la réponse acceptée de la seule réponse Python 2 de Martelli.
Antti Haapala

Réponses:

113

La .encodeméthode est appliquée à une chaîne Unicode pour créer une chaîne d'octets; mais vous l'appelez plutôt sur une chaîne d'octets ... dans le mauvais sens! Regardez le codecsmodule dans la bibliothèque standard et codecs.openen particulier pour de meilleures solutions générales pour la lecture de fichiers texte encodés en UTF-8. Cependant, pour le csvmodule 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 dans utf-8, mais par exemple dans ISO-8859-1, alors vous avez besoin d'un "transcodage" (si vous souhaitez utiliser utf-8 au csvniveau du module) , de la forme line.decode('whateverweirdcodec').encode('utf-8')- mais vous pouvez probablement simplement utiliser le nom de votre encodage existant dans la yieldligne de mon code ci-dessus, au lieu de 'utf-8', comme cela csvva en fait être très bien avec les bytestrings encodés ISO-8859- *.

Alex Martelli
la source
4
Cela signifie-t-il que l'exemple dans la documentation python (d'où OP copié et collé) est faux? Quel est l'intérêt de l'étape d'encodage supplémentaire si elle se rompt lorsque vous lui donnez un csv unicode?
Anentropic
80

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 naviguer pour écrire un nouveau code lié à csv.

Voici un exemple 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 par le csvmodule intégré. 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)
jb.
la source
8

Si vous souhaitez lire un fichier CSV avec un encodage utf-8, une approche minimaliste que je vous recommande est d'utiliser quelque chose comme ceci:

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

Avec cette déclaration, vous pouvez utiliser plus tard un lecteur CSV pour travailler avec.

Nick Cuevas
la source
2
Est-il possible qu'il ne s'agisse que de Python 3? Il échoue pour moi, en Python 2. Il n'accepte pas encodingenopen
Zvika
@Zvika oui, en python 3 cette solution fonctionne:open('file.csv', 'r', encoding="ISO8859")
luca76
J'ajouterais également open (file_name, "rt", encoding = 'utf-8'), c'est-à-dire ouvrir le fichier en mode "read text"
Jimmy Lee Jones le
3

Consultez également la réponse dans cet article: https://stackoverflow.com/a/9347871/1338557

Il suggère l'utilisation de la bibliothèque appelée ucsv.py. Remplacement court et simple pour CSV écrit pour résoudre le problème d'encodage (utf-8) pour Python 2.7. Fournit également un support pour csv.DictReader

Edit : Ajout d'un exemple de code que j'ai utilisé:

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
la source
vous devriez mettre quelques détails sur ce lien dans votre réponse, juste au cas où le lien
serait
# Downvoter - Je ne sais pas pourquoi vous pensiez que c'était inutile. La bibliothèque ucsv a très bien fonctionné pour moi. J'ai aidé à résoudre l'erreur unicde avec laquelle je luttais depuis 2 jours. Si vous cherchiez un exemple de code, le voici dans l'édition @ Yaje- J'ai donné quelques détails; également l'exemple de code. Et corrigé le lien également, qui pointait plus tôt vers un autre message.
Atripavan
Une raison particulière pour laquelle vous ouvrez un fichier texte en tant que binaire? «rb» sert à ouvrir les fichiers binaires.
Codeguy007
2

Utiliser codecs.opencomme Alex Martelli l'a suggéré 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 ...
user1154664
la source
3
Cela ne fonctionnerait pas avec tous les CSV, voici une ligne csv valide: "Foo Bar; Baz"; 231; 313; ";;;"; 1;
jb.
Vous importez le csvmodule mais ne l'utilisez pas.
Christophe Roussy
1

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 la version 2.5 (en plus des corrections de bogues). Voici le code qui fonctionne simplement sans aucun encodage / décodage (le fichier da.csv contient les mêmes données que les données variables ). Je suppose que votre fichier doit ê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
van
la source
Je me demande dans quelle version de python cela fonctionnerait? J'obtiens des erreurs avec 2.7 et 3.5. "ValueError: pas assez de valeurs à décompresser (attendu 3, obtenu 1)"
eis
@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.
van
1

Il convient de noter que si rien n'a fonctionné pour vous, vous avez peut-être oublié d'échapper à votre chemin.
Par exemple, ce code:

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

Entraînerait une erreur:

SyntaxError: (erreur unicode) le codec 'unicodeescape' ne peut pas décoder les octets en position 2-3: échappement \ UXXXXXXXX tronqué

Pour réparer, faites simplement:

f = open("C:\\Some\\Path\\To\\file.csv")
OfirD
la source
0

En regardant la Latin-1table unicode , je vois le code de caractère 00E9" LATIN MINUSCULE LETTRE E AVEC ACUTE ". Il s'agit du caractère accentué de vos exemples de données. Un simple test Pythonmontre que le UTF-8codage de ce caractère est différent du UTF-16codage unicode (presque ).

>>> 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 le spécial unicode_csv_reader(). La simple lecture des données d'un fichier peut masquer l'encodage, alors vérifiez les valeurs réelles des caractères.

gimel
la source
0

A eu le même problème sur un autre serveur, mais s'est rendu compte que les paramètres régionaux sont perturbés.

export LC_ALL="en_US.UTF-8"

résolu le problème

Piotr Pęczek
la source