Comment écrire une ligne d'en-tête avec csv.DictWriter?

Supposons que j'ai un objet csv.DictReader et que je veux l'écrire en tant que fichier CSV. Comment puis-je faire cela?

Je sais que je peux écrire le lignes de données comme ceci:

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)

Mais comment puis-je inclure les noms de champs?

93
demandé sur martineau 2010-06-06 00:41:26

3 réponses

Modifier:
Dans 2.7 / 3.2 il y a une nouvelle méthode writeheader() . En outre, la réponse de John Machin fournit une méthode plus simple d'écriture de la ligne d'en-tête.
Exemple Simple d'utilisation de la méthode 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.
À partir de la documentation:

Le paramètre fieldnames identifie l'ordre dans lequel les valeurs de la dictionnaire passé à l'writerow() la méthode sont écrit à la csvfile.

Autrement dit: L'argument Fieldnames est requis car les dicts Python sont intrinsèquement 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: l'instruction with a été ajoutée en 2.6. Si vous utilisez 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 @ FM le mentionne dans un commentaire, vous pouvez condenser l'écriture d'en-tête dans un one-liner, par exemple:

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)
124
répondu bernie 2012-02-07 20:20:35

Quelques options:

(1)Faites laborieusement un dict de mappage d'identité (c'est-à-dire do-nothing) à partir de vos noms de champ afin que csv.DictWriter peut le convertir en une liste et le passer à un csv.l'instance du rédacteur.

(2) la documentation mentionne "l'instance writer sous-jacente"... alors utilisez-le simplement (exemple à la fin).

dw.writer.writerow(dw.fieldnames)

(3) Évitez le csv.Dictwriter overhead et faites-le vous-même avec csv.Ecrivain

Écriture des données:

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

Ou

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

Au lieu du extrasaction "fonctionnalité", je préfère le coder moi-même; de cette façon, vous pouvez signaler tous les" extras " avec les clés et les valeurs, pas seulement la première clé supplémentaire. Ce qui est une véritable nuisance avec DictWriter, c'est que si vous avez vérifié les clés vous-même pendant que chaque dict était en cours de construction, vous devez vous rappeler d'utiliser extrasaction= 'ignore' sinon ça va lentement (fieldnames est une liste) répétez 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'
>>>
26
répondu John Machin 2010-06-06 00:13:08

Une Autre façon de faire serait d'ajouter avant d'ajouter les lignes de 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 lancer un dictionnaire.

9
répondu Raphael Pr 2012-11-21 15:51:38