109 votes

écriture d'un en-tête dans un csv en python avec DictWriter

Supposons que j'ai un objet csv.DictReader et que je veux l'écrire dans un fichier csv. Comment puis-je le faire ?

J'ai pensé à ce qui suit :

dr = csv.DictReader(open(f), delimiter='\t')
# process my dr object
# ...
# write out object
output = csv.DictWriter(open(f2, 'w'), delimiter='\t')
for item in dr:
  output.writerow(item)

Est-ce le meilleur moyen ?

Plus important encore, comment puis-je faire en sorte qu'un en-tête soit également écrit, dans ce cas la propriété .fieldnames de l'objet "dr" ?

Merci.

141voto

bernie Points 44206

Editar:
Dans 2.7 / 3.2 il y a une nouvelle writeheader() méthode . Par ailleurs, la réponse de John Machin propose une méthode plus simple pour écrire la ligne d'en-tête.
Exemple simple d'utilisation du writeheader() maintenant disponible en 2.7 / 3.2 :

from collections import OrderedDict
ordered_fieldnames = OrderedDict([('field1',None),('field2',None)])
with open(outfile,'wb') as fou:
    dw = csv.DictWriter(fou, delimiter='\t', fieldnames=ordered_fieldnames)
    dw.writeheader()
    # continue on to write data

L'instanciation de DictWriter nécessite un argument fieldnames.
De la documentation :

Le paramètre fieldnames identifie l'ordre dans lequel les valeurs du dictionnaire dictionnaire transmis à la méthode writerow() sont écrites dans le fichier csvfile.

En d'autres termes : L'argument Fieldnames est nécessaire parce que les dicts Python sont par nature non ordonnés.
Voici un exemple de la façon dont vous écrivez l'en-tête et les données dans un fichier.
Note : with a été ajoutée dans la version 2.6. Si vous utilisez la version 2.5 : from __future__ import with_statement

with open(infile,'rb') as fin:
    dr = csv.DictReader(fin, delimiter='\t')

# dr.fieldnames contains values from first row of `f`.
with open(outfile,'wb') as fou:
    dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames)
    headers = {} 
    for n in dw.fieldnames:
        headers[n] = n
    dw.writerow(headers)
    for row in dr:
        dw.writerow(row)

Comme le mentionne @FM dans un commentaire, vous pouvez condenser l'écriture de l'en-tête en une seule ligne, par ex :

with open(outfile,'wb') as fou:
    dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames)
    dw.writerow(dict((fn,fn) for fn in dr.fieldnames))
    for row in dr:
        dw.writerow(row)

28voto

John Machin Points 39706

Quelques options :

(1) Faire laborieusement un dict de mappage d'identité (i.e. ne rien faire) à partir de vos noms de champs pour que csv.DictWriter puisse le reconvertir en liste et le passer à une instance de csv.writer.

(2) La documentation mentionne "le sous-jacent writer instance" ... il suffit donc de l'utiliser (exemple à la fin).

dw.writer.writerow(dw.fieldnames)

(3) Évitez la surcharge de csv.Dictwriter et faites-le vous-même avec csv.writer.

Écriture de données :

w.writerow([d[k] for k in fieldnames])

ou

w.writerow([d.get(k, restval) for k in fieldnames])

Au lieu de la extrasaction "fonctionnalité", je préférerais la coder moi-même ; de cette façon, vous pouvez signaler TOUS les "extras" avec les clés et les valeurs, et pas seulement la première clé supplémentaire. Ce qui est vraiment ennuyeux avec DictWriter, c'est que si vous avez vérifié les clés vous-même lors de la construction de chaque dict, vous devez vous rappeler d'utiliser extrasaction='ignore' sinon il va répéter LENTEMENT (les noms de champs sont une liste) la vérification :

wrong_fields = [k for k in rowdict if k not in self.fieldnames]

\============

>>> f = open('csvtest.csv', 'wb')
>>> import csv
>>> fns = 'foo bar zot'.split()
>>> dw = csv.DictWriter(f, fns, restval='Huh?')
# dw.writefieldnames(fns) -- no such animal
>>> dw.writerow(fns) # no such luck, it can't imagine what to do with a list
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\python26\lib\csv.py", line 144, in writerow
    return self.writer.writerow(self._dict_to_list(rowdict))
  File "C:\python26\lib\csv.py", line 141, in _dict_to_list
    return [rowdict.get(key, self.restval) for key in self.fieldnames]
AttributeError: 'list' object has no attribute 'get'
>>> dir(dw)
['__doc__', '__init__', '__module__', '_dict_to_list', 'extrasaction', 'fieldnam
es', 'restval', 'writer', 'writerow', 'writerows']
# eureka
>>> dw.writer.writerow(dw.fieldnames)
>>> dw.writerow({'foo':'oof'})
>>> f.close()
>>> open('csvtest.csv', 'rb').read()
'foo,bar,zot\r\noof,Huh?,Huh?\r\n'
>>>

9voto

Raphael Pr Points 111

Une autre façon de procéder serait d'ajouter avant d'ajouter des lignes dans votre sortie, la ligne suivante :

output.writerow(dict(zip(dr.fieldnames, dr.fieldnames)))

Le zip renverrait une liste de doublets contenant la même valeur. Cette liste pourrait être utilisée pour initier un dictionnaire.

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