web-dev-qa-db-fra.com

Python CSV DictReader avec données UTF-8

Autant que je sache, le module csv Python (v2.6) ne peut pas gérer les données Unicode par défaut, correct? Dans la documentation Python, il existe un exemple sur la façon de lire un fichier encodé en UTF-8. Mais cet exemple ne renvoie que les lignes CSV sous forme de liste. J'aimerais accéder aux colonnes de lignes par leur nom, comme cela est fait par csv.DictReader mais avec un fichier d'entrée au format CSV codé UTF-8.

Quelqu'un peut-il me dire comment faire cela de manière efficace? Je vais devoir traiter des fichiers CSV avec une taille de 100 Mo.

27
LMatter

Je suis venu avec une réponse moi-même:

def UnicodeDictReader(utf8_data, **kwargs):
    csv_reader = csv.DictReader(utf8_data, **kwargs)
    for row in csv_reader:
        yield {unicode(key, 'utf-8'):unicode(value, 'utf-8') for key, value in row.iteritems()}

Note: Ceci a été mis à jour pour que les clés soient décodées selon la suggestion dans les commentaires

48
LMatter

Une approche classifiée de la réponse @LMatter. Cette approche vous permet de bénéficier de tous les avantages de DictReader, tels que l’obtention des noms de champs et l’obtention du numéro de ligne, ainsi que la prise en charge du format UTF-8.

import csv

class UnicodeDictReader(csv.DictReader, object):

    def next(self):
        row = super(UnicodeDictReader, self).next()
        return {unicode(key, 'utf-8'): unicode(value, 'utf-8') for key, value in row.iteritems()}
0
Kasa

Tout d’abord, utilisez la version 2.6 de la documentation . Cela peut changer pour chaque version. Il indique clairement qu'il ne prend pas en charge Unicode, mais qu'il prend en charge UTF-8. Techniquement , ce ne sont pas la même chose. Comme le dit la documentation:

Le module csv ne prend pas directement en charge la lecture et l’écriture en Unicode, mais c’est une sauvegarde 8 bits en cas de problème avec certains problèmes liés aux caractères ASCII NUL. Vous pouvez donc écrire des fonctions ou des classes qui gèrent l'encodage et le décodage pour vous, à condition d'éviter les encodages tels que UTF-16 qui utilisent des NUL. UTF-8 est recommandé.

L'exemple ci-dessous (tiré de la documentation) montre comment créer deux fonctions qui lisent correctement le texte au format UTF-8 au format CSV. Vous devez savoir que csv.reader() renvoie toujours un objet DictReader.

import csv

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.DictReader(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]
0
kelloti

La réponse n'a pas les méthodes DictWriter, alors voici la classe mise à jour:

class DictUnicodeWriter(object):

    def __init__(self, f, fieldnames, dialect=csv.Excel, encoding="utf-8", **kwds):
        self.fieldnames = fieldnames    # list of keys for the dict
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.DictWriter(self.queue, fieldnames, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        self.writer.writerow({k: v.encode("utf-8") for k, v in row.items()})
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for row in rows:
            self.writerow(row)

    def writeheader(self):
        header = dict(Zip(self.fieldnames, self.fieldnames))
        self.writerow(header)
0
SePo

Le package csvw a également d'autres fonctionnalités (pour les fichiers CSV enrichis de métadonnées pour le Web), mais il définit une classe UnicodeDictReader entourant sa classe UnicodeReader, ce qui en fait exactement ce qui suit:

class UnicodeReader(Iterator):
    """Read Unicode data from a csv file."""
    […]

    def _next_row(self):
        self.lineno += 1
        return [
            s if isinstance(s, text_type) else s.decode(self._reader_encoding)
            for s in next(self.reader)]

Il m’a attrapé quelques fois, mais csvw.UnicodeDictReader vraiment, vraiment doit être utilisé dans un bloc with et se casse autrement. Autre que cela, le module est bien générique et compatible avec py2 et py3.

0
Anaphory