web-dev-qa-db-fra.com

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). Basé sur la documentation Python 2.5 du csvreader ( http://docs.python.org/library/csv.html ), je suis arrivé avec le code suivant pour lire le fichier CSV puisque 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:
        ...

Ci-dessous, un extrait du fichier CSV que je tente 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?

86
Martin

La méthode .encode 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 module codecs de la bibliothèque standard et codecs.open En particulier pour de meilleures solutions générales pour la lecture de fichiers texte codés en UTF-8. Cependant, pour le module 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 dans utf-8, mais par ex. Dans ISO-8859-1, vous avez besoin d'un "transcodage" (si vous souhaitez utiliser utf-8 au niveau du module csv), de la forme line.decode('whateverweirdcodec').encode('utf-8') - mais vous pouvez probablement simplement utiliser le nom de votre encodage existant dans la ligne yield de mon code ci-dessus, au lieu de 'utf-8', car csv ira très bien avec Chaînes d'octets codées ISO-8859- *.

109
Alex Martelli

Python 2.X

Il existe une bibliothèque nicode-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

Dans python 3, cela est pris en charge immédiatement par le module intégré csv. 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)
71
jb.

Vérifiez également la réponse dans ce message: https://stackoverflow.com/a/9347871/1338557

Il suggère d'utiliser une bibliothèque appelée ucsv.py. Remplacement simple et rapide de fichiers CSV écrits pour résoudre le problème de codage (utf-8) de Python 2.7. Prend également en charge csv.DictReader.

Edit : Ajout du code exemple 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'])})
3
Atripavan

Si vous voulez lire un fichier CSV avec le codage utf-8, une approche minimaliste que je vous recommande consiste à utiliser quelque chose comme ceci:

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

Avec cette déclaration, vous pouvez utiliser ultérieurement un lecteur CSV avec lequel travailler.

3
Nick Cuevas

En utilisant codecs.open comme suggéré par 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 ...
2
user1154664

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 corrections de bogues). Voici le code qui fonctionne sans aucun encodage/décodage (le fichier da.csv contient les mêmes données que la variable data ). Je suppose que votre fichier doit être lu correctement, sans 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
1
van

Avait le même problème sur un autre serveur, mais a réalisé que les paramètres régionaux sont perturbés.

export LC_ALL="en_US.UTF-8"

résolu le problème

0
Piotr Pęczek

En regardant la Latin-1 Table unicode , je vois le code de caractère 00E9 " LETTRE MINUSCULE LATINE E AVEC AIGU ". C'est le caractère accentué dans vos exemples de données. Un simple test dans Python montre que le codage UTF-8 De ce caractère est différent du codage unicode (presque UTF-16).

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

Je vous suggère d'essayer de 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 le codage. Vérifiez donc les valeurs réelles des caractères.

0
gimel