csv.DictWriter
peut écrire une liste de dictionnaires dans un fichier CSV.csv
ne peut pas gérer la conversion de chaînes unicode en UTF8.La documentation du module csv
contient un exemple pour tout convertir en UTF8:
def utf_8_encoder(unicode_csv_data):
for line in unicode_csv_data:
yield line.encode('utf-8')
Il a également une classe UnicodeWriter
.
Mais ... comment faire fonctionner DictWriter
avec ceux-ci? Ne devraient-ils pas s'injecter au milieu de celui-ci, attraper les dictionnaires désassemblés et les encoder avant qu'il ne les écrive dans le fichier? Je ne comprends pas.
[~ # ~] mise à jour [~ # ~] : le module tiers nicodecsv implémente cette réponse vieille de 7 ans pour vous. Exemple sous ce code. Il existe également une solution Python 3 qui ne nécessite pas de module tiers.
Original Python 2 Réponse
Si vous utilisez Python 2.7 ou version ultérieure, utilisez une compréhension dict pour remapper le dictionnaire vers utf-8 avant de passer à DictWriter:
# coding: utf-8
import csv
D = {'name':u'马克','pinyin':u'mǎkè'}
f = open('out.csv','wb')
f.write(u'\ufeff'.encode('utf8')) # BOM (optional...Excel needs it to open UTF-8 file properly)
w = csv.DictWriter(f,sorted(D.keys()))
w.writeheader()
w.writerow({k:v.encode('utf8') for k,v in D.items()})
f.close()
Vous pouvez utiliser cette idée pour mettre à jour UnicodeWriter vers DictUnicodeWriter: # coding: utf-8 import csv import cStringIO import codecs
class DictUnicodeWriter(object):
def __init__(self, f, fieldnames, dialect=csv.Excel, encoding="utf-8", **kwds):
# 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, D):
self.writer.writerow({k:v.encode("utf-8") for k,v in D.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 D in rows:
self.writerow(D)
def writeheader(self):
self.writer.writeheader()
D1 = {'name':u'马克','pinyin':u'Mǎkè'}
D2 = {'name':u'美国','pinyin':u'Měiguó'}
f = open('out.csv','wb')
f.write(u'\ufeff'.encode('utf8')) # BOM (optional...Excel needs it to open UTF-8 file properly)
w = DictUnicodeWriter(f,sorted(D.keys()))
w.writeheader()
w.writerows([D1,D2])
f.close()
Python 2 nicodecsv Exemple:
# coding: utf-8
import unicodecsv as csv
D = {u'name':u'马克',u'pinyin':u'mǎkè'}
with open('out.csv','wb') as f:
w = csv.DictWriter(f,fieldnames=sorted(D.keys()),encoding='utf-8-sig')
w.writeheader()
w.writerow(D)
Python 3:
De plus, Python 3 prend en charge Unicode en natif:
# coding: utf-8
import csv
D = {u'name':u'马克',u'pinyin':u'mǎkè'}
# Use newline='' instead of 'wb' in Python 3.
with open('out.csv','w',encoding='utf-8-sig',newline='') as f:
w = csv.DictWriter(f,fieldnames=sorted(D.keys()))
w.writeheader()
w.writerow(D)
Il existe une solution de contournement simple en utilisant le merveilleux module nicodeCSV . Après l'avoir, il suffit de changer de ligne
import csv
à
import unicodecsv as csv
Et il commence automatiquement à jouer à Nice avec UTF-8.
Remarque: Passer à Python 3 vous débarrassera également de ce problème (merci jamescampbell et c'est quelque chose que l'on devrait faire de toute façon.
Vous pouvez convertir les valeurs en UTF-8 à la volée lorsque vous passez le dict à DictWriter.writerow()
. Par exemple:
import csv
rows = [
{'name': u'Anton\xedn Dvo\u0159\xe1k','country': u'\u010cesko'},
{'name': u'Bj\xf6rk Gu\xf0mundsd\xf3ttir', 'country': u'\xcdsland'},
{'name': u'S\xf8ren Kierkeg\xe5rd', 'country': u'Danmark'}
]
# implement this wrapper on 2.6 or lower if you need to output a header
class DictWriterEx(csv.DictWriter):
def writeheader(self):
header = dict(Zip(self.fieldnames, self.fieldnames))
self.writerow(header)
out = open('foo.csv', 'wb')
writer = DictWriterEx(out, fieldnames=['name','country'])
# DictWriter.writeheader() was added in 2.7 (use class above for <= 2.6)
writer.writeheader()
for row in rows:
writer.writerow(dict((k, v.encode('utf-8')) for k, v in row.iteritems()))
out.close()
Sortie foo.csv:
name,country
Antonín Dvořák,Česko
Björk Guðmundsdóttir,Ísland
Søren Kierkegård,Danmark
Vous pouvez utiliser une classe proxy pour coder les valeurs dict selon les besoins, comme ceci:
# -*- coding: utf-8 -*-
import csv
d = {'a':123,'b':456, 'c':u'Non-ASCII: проверка'}
class DictUnicodeProxy(object):
def __init__(self, d):
self.d = d
def __iter__(self):
return self.d.__iter__()
def get(self, item, default=None):
i = self.d.get(item, default)
if isinstance(i, unicode):
return i.encode('utf-8')
return i
with open('some.csv', 'wb') as f:
writer = csv.DictWriter(f, ['a', 'b', 'c'])
writer.writerow(DictUnicodeProxy(d))
Quand vous appelez csv.writer
avec votre contenu, l'idée est de faire passer le contenu par utf_8_encoder
car cela vous donnerait le contenu encodé (utf-8).
Ma solution est un peu différente. Bien que toutes les solutions ci-dessus se concentrent sur la dictée compatible Unicode, mes solutions rendent DictWriter compatible avec Unicode. Cette approche est même suggérée dans python docs ( 1 ).
Les classes UTF8Recoder, UnicodeReader, UnicodeWriter sont extraites de python docs. UnicodeWriter-> writerow a également été légèrement modifié.
Utilisez-le comme DictWriter/DictReader ordinaire.
Voici le code:
import csv, codecs, cStringIO
class UTF8Recoder:
"""
Iterator that reads an encoded stream and reencodes the input to UTF-8
"""
def __init__(self, f, encoding):
self.reader = codecs.getreader(encoding)(f)
def __iter__(self):
return self
def next(self):
return self.reader.next().encode("utf-8")
class UnicodeReader:
"""
A CSV reader which will iterate over lines in the CSV file "f",
which is encoded in the given encoding.
"""
def __init__(self, f, dialect=csv.Excel, encoding="utf-8", **kwds):
f = UTF8Recoder(f, encoding)
self.reader = csv.reader(f, dialect=dialect, **kwds)
def next(self):
row = self.reader.next()
return [unicode(s, "utf-8") for s in row]
def __iter__(self):
return self
class UnicodeWriter:
"""
A CSV writer which will write rows to CSV file "f",
which is encoded in the given encoding.
"""
def __init__(self, f, dialect=csv.Excel, encoding="utf-8", **kwds):
# Redirect output to a queue
self.queue = cStringIO.StringIO()
self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
self.stream = f
self.encoder = codecs.getincrementalencoder(encoding)()
def writerow(self, row):
self.writer.writerow([unicode(s).encode("utf-8") for s in row])
# 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)
class UnicodeDictWriter(csv.DictWriter, object):
def __init__(self, f, fieldnames, restval="", extrasaction="raise", dialect="Excel", *args, **kwds):
super(UnicodeDictWriter, self).__init__(f, fieldnames, restval="", extrasaction="raise", dialect="Excel", *args, **kwds)
self.writer = UnicodeWriter(f, dialect, **kwds)