Làm thế nào để viết hàng tiêu đề với csv.DictWriter?


114

Giả sử tôi có một csv.DictReaderđối tượng và tôi muốn ghi nó ra dưới dạng tệp CSV. Tôi có thể làm cái này như thế nào?

Tôi biết rằng tôi có thể viết các hàng dữ liệu như sau:

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)

Nhưng làm thế nào tôi có thể bao gồm các tên trường?

Câu trả lời:


149

Chỉnh sửa:
Trong 2.7 / 3.2 có một writeheader()phương thức mới . Ngoài ra, câu trả lời của John Machin cung cấp một phương pháp đơn giản hơn để viết hàng tiêu đề.
Ví dụ đơn giản về việc sử dụng writeheader()phương pháp hiện có sẵn trong 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

Khởi tạo DictWriter yêu cầu một đối số tên trường.
Từ tài liệu :

Tham số fieldnames xác định thứ tự mà các giá trị trong từ điển được truyền cho phương thức writerow () được ghi vào csvfile.

Nói một cách khác: Đối số Fieldnames là bắt buộc vì các phân đoạn trong Python vốn không có thứ tự.
Dưới đây là ví dụ về cách bạn ghi tiêu đề và dữ liệu vào tệp.
Lưu ý: withcâu lệnh đã được thêm trong 2.6. Nếu sử dụng 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)

Như @FM đã đề cập trong một nhận xét, bạn có thể cô đọng phần viết tiêu đề thành một lớp lót, ví dụ:

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)

12
1 Tuy nhiên, một cách khác để viết tiêu đề: dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc

2
@Adam: để biết một chữ lót ngắn hơn, hãy xem câu trả lời của tôi.
John Machin

2
@John: +1 câu trả lời của bạn; chỉ cần sử dụng "ví dụ người viết cơ bản" chắc chắn sẽ thích hơn "lập bản đồ danh tính tốn công sức".
Mechanical_meat

1
@endolith: cảm ơn bạn đã phản hồi. Đã chuyển phần đó lên đầu câu trả lời.
Mechanical_meat,

1
Vì bạn cũng đang sử dụng dictReader, nên bạn có thể dễ dàng thêm các trường với dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). Bằng cách đó, nếu các trường của bạn thay đổi, bạn không cần phải điều chỉnh dictWriter.
Spencer Rathbun

29

Một số tùy chọn:

(1) Thực hiện một cách chăm chỉ ánh xạ danh tính (tức là không làm gì) ra khỏi tên trường của bạn để csv.DictWriter có thể chuyển đổi nó trở lại danh sách và chuyển nó đến một phiên bản csv.writer.

(2) Tài liệu đề cập đến " writertrường hợp cơ bản " ... vì vậy chỉ cần sử dụng nó (ví dụ ở cuối).

dw.writer.writerow(dw.fieldnames)

(3) Tránh chi phí csv.Dictwriter và tự mình làm điều đó với csv.writer

Ghi dữ liệu:

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

hoặc là

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

Thay vì extrasaction"chức năng", tôi muốn tự mình viết mã nó; bằng cách đó, bạn có thể báo cáo TẤT CẢ "tính năng bổ sung" với các khóa và giá trị, không chỉ khóa phụ đầu tiên. Một điều phiền toái thực sự với DictWriter là nếu bạn đã tự mình xác minh các khóa khi mỗi lệnh được xây dựng, bạn cần nhớ sử dụng extrasaction = 'ignore' nếu không nó sẽ CHẬM (tên trường là một danh sách), hãy lặp lại kiểm tra:

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'
>>>

Hiện tại trong Python 3.6, extrasactionchức năng dường như được triển khai tốt hơn. Bây giờ nó đã được thiết wrong_fields = rowdict.keys() - self.fieldnames so it's effectively a lập hoạt động.
martineau

Tôi bỏ phiếu câu trả lời này lên cho 'tránh DictWriter' bình luận - Tôi chưa thấy bất kỳ lợi thế để sử dụng nó, và dường như nhanh hơn để cấu trúc dữ liệu của bạn và sử dụng csv.writer
neophytte

8

Một cách khác để làm điều này là thêm trước khi thêm các dòng trong đầu ra của bạn, dòng sau:

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

Zip sẽ trả về một danh sách các doublet có cùng giá trị. Danh sách này có thể được sử dụng để bắt đầu một từ điển.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.