Bagaimana cara menulis baris header dengan csv.DictWriter?

114

Asumsikan saya memiliki sebuah csv.DictReaderobjek dan saya ingin menuliskannya sebagai file CSV. Bagaimana saya bisa melakukan ini?

Saya tahu bahwa saya dapat menulis baris data seperti ini:

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)

Tapi bagaimana saya bisa memasukkan nama field?

martineau.dll
sumber

Jawaban:

149

Edit:
Di 2.7 / 3.2 ada metode baruwriteheader() . Juga, jawaban John Machin memberikan metode yang lebih sederhana untuk menulis baris tajuk.
Contoh sederhana menggunakan writeheader()metode ini sekarang tersedia di 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

Membuat instance DictWriter membutuhkan argumen nama bidang.
Dari dokumentasi :

Parameter nama bidang mengidentifikasi urutan di mana nilai dalam kamus yang diteruskan ke metode writerow () ditulis ke csvfile.

Dengan kata lain: Argumen Fieldnames diperlukan karena dicts Python tidak berurutan.
Di bawah ini adalah contoh bagaimana Anda akan menulis header dan data ke sebuah file.
Catatan: withpernyataan telah ditambahkan di 2.6. Jika menggunakan 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)

Seperti yang @FM sebutkan dalam komentar, Anda dapat meringkas penulisan header menjadi satu baris, misalnya:

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)
mechanical_meat
sumber
12
1 Namun cara lain untuk menulis header: dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc
2
@Adam: untuk satu baris yang lebih pendek, lihat jawaban saya.
John Machin
2
@ John: +1 untuk jawaban Anda; hanya menggunakan "contoh penulis yang mendasari" tentu lebih baik daripada "pemetaan identitas yang melelahkan".
mechanical_meat
1
@endolith: terima kasih atas umpan baliknya. Pindahkan bagian itu ke atas jawaban.
mechanical_meat
1
Karena Anda juga menggunakan dictReader, maka mudah untuk menambahkan bidang dengan dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). Dengan begitu, jika field Anda berubah, Anda tidak perlu mengatur dictWriter.
Spencer Rathbun
29

Beberapa pilihan:

(1) Dengan susah payah membuat pemetaan identitas (yaitu tidak melakukan apa-apa) dari nama bidang Anda sehingga csv.DictWriter dapat mengubahnya kembali ke daftar dan meneruskannya ke instance csv.writer.

(2) Dokumentasi menyebutkan " writercontoh yang mendasari " ... jadi gunakan saja (contoh di akhir).

dw.writer.writerow(dw.fieldnames)

(3) Hindari overhead csv.Dictwriter dan lakukan sendiri dengan csv.writer

Menulis data:

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

atau

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

Alih-alih extrasaction"fungsionalitas", saya lebih suka membuat kode sendiri; dengan cara itu Anda dapat melaporkan SEMUA "ekstra" dengan kunci dan nilai, bukan hanya kunci ekstra pertama. Apa gangguan nyata dengan DictWriter adalah bahwa jika Anda telah memverifikasi kunci sendiri saat setiap dict sedang dibuat, Anda harus ingat untuk menggunakan extrasaction = 'ignore' jika tidak, itu akan menjadi SLOWLY (nama bidang adalah daftar) ulangi pemeriksaan:

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
sumber
Saat ini di Python 3.6, extrasactionfungsinya tampaknya diterapkan dengan lebih baik. Sekarang wrong_fields = rowdict.keys() - self.fieldnames so it's effectively a operasi disetel.
martineau
Saya memberikan suara untuk jawaban ini untuk komentar 'hindari DictWriter' - saya belum melihat keuntungan apa pun untuk menggunakannya, dan tampaknya lebih cepat menyusun data Anda dan menggunakan csv.writer
neophytte
8

Cara lain untuk melakukan ini adalah dengan menambahkan sebelum menambahkan baris dalam output Anda, baris berikut:

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

Zip akan mengembalikan daftar doublet yang berisi nilai yang sama. Daftar ini dapat digunakan untuk memulai kamus.

Raphael Pr
sumber