Jak napisać wiersz nagłówka za pomocą csv.DictWriter?

114

Załóżmy, że mam csv.DictReaderobiekt i chcę go zapisać jako plik CSV. W jaki sposób mogę to zrobić?

Wiem, że mogę zapisać wiersze danych w ten sposób:

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)

Ale jak mogę dołączyć nazwy pól?

martineau
źródło

Odpowiedzi:

149

Edycja:
W 2.7 / 3.2 jest nowa writeheader()metoda . Również odpowiedź Johna Machina zapewnia prostszą metodę pisania wiersza nagłówka.
Prosty przykład użycia writeheader()metody dostępnej teraz w 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

Utworzenie wystąpienia DictWriter wymaga argumentu nazwy pól.
Z dokumentacji :

Parametr fieldnames określa kolejność, w jakiej wartości ze słownika przekazane do metody writerow () są zapisywane do pliku csvfile.

Innymi słowy: argument Fieldnames jest wymagany, ponieważ dykty w Pythonie są z natury nieuporządkowane.
Poniżej znajduje się przykład, jak zapisać nagłówek i dane do pliku.
Uwaga: withoświadczenie zostało dodane w 2.6. Jeśli używasz 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)

Jak wspomina @FM w komentarzu, możesz skondensować pisanie nagłówka do jednowierszowego tekstu, np .:

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)
mięso_mechaniczne
źródło
12
+1 Jeszcze innym sposobem, aby napisać nagłówek: dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc
2
@Adam: jeśli chodzi o krótszy, jednolinijkowy tekst, zobacz moją odpowiedź.
John Machin,
2
@John: +1 do Twojej odpowiedzi; po prostu użycie „podstawowej instancji pisarza” jest z pewnością lepsze niż „pracochłonne mapowanie tożsamości”.
Mechanical_meat
1
@endolith: dzięki za informację zwrotną. Przeniesiono tę część na początek odpowiedzi.
Mechanical_meat
1
Ponieważ używasz również dictReader, możesz łatwo dodać pola za pomocą dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). W ten sposób, jeśli zmienią się twoje pola, nie musisz dostosowywać dictWriter.
Spencer Rathbun,
29

Kilka opcji:

(1) Pracochłonnie wykonaj mapowanie tożsamości (tj. Nic nie rób) z nazw pól, aby csv.DictWriter mógł przekształcić je z powrotem w listę i przekazać do instancji csv.writer.

(2) Dokumentacja wspomina o „podstawowej writerinstancji”… więc po prostu go użyj (przykład na końcu).

dw.writer.writerow(dw.fieldnames)

(3) Unikaj narzutu csv.Dictwriter i zrób to sam z csv.writer

Zapisywanie danych:

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

lub

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

Zamiast extrasaction„funkcjonalności” wolałbym sam go zakodować; w ten sposób możesz zgłosić WSZYSTKIE „dodatki” za pomocą kluczy i wartości, a nie tylko pierwszego dodatkowego klucza. To, co jest naprawdę uciążliwe w przypadku DictWriter, polega na tym, że jeśli samodzielnie zweryfikowałeś klucze podczas budowania każdego dyktu, musisz pamiętać, aby użyć extrasaction = 'ignore', w przeciwnym razie nastąpi POWOLI (nazwy pól to lista) powtórz sprawdzanie:

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'
>>>
John Machin
źródło
Obecnie w Pythonie 3.6 extrasactionfunkcjonalność wydaje się być zaimplementowana lepiej. To jest teraz wrong_fields = rowdict.keys() - self.fieldnames so it's effectively a ustawione.
martineau
Głosuję na tę odpowiedź za komentarz `` unikaj DictWriter '' - nie widziałem żadnej korzyści z jej używania i wydaje się, że szybciej ustrukturyzuję twoje dane i użyj csv.writer
neophytte
8

Innym sposobem na zrobienie tego byłoby dodanie przed dodaniem wierszy do wyniku, następującego wiersza:

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

ZIP zwróciłby listę dubletów zawierającą tę samą wartość. Ta lista może posłużyć do zainicjowania słownika.

Raphael Pr
źródło