如何使用csv.DictWriter编写标题行?

131
假设我有一个csv.DictReader对象,我想将它写成一个CSV文件。我该如何做?
我知道可以像这样写入数据的行:
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)

但是我该如何包含字段名?

4个回答

175

编辑:
在2.7 / 3.2版本中,有一个新的writeheader()方法。此外,John Machin的答案提供了一种更简单的方法来编写标题行。
以下是使用2.7 / 3.2版本中现有的writeheader()方法的简单示例:

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

实例化DictWriter需要一个fieldnames参数。
根据文档的说明:

fieldnames参数标识了传递给writerow()方法的字典中值写入到csvfile的顺序。

换句话说:Fieldnames参数是必需的,因为Python字典本质上是无序的。
以下是如何将标题和数据写入文件的示例。
注意:with语句在2.6中添加。如果使用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)

正如@FM在评论中提到的那样,您可以将标题编写压缩为一行代码,例如:

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)

13
另一种编写标题的方式是:dw.writerow( dict((f,f) for f in dr.fieldnames) ) - FMc
2
@Adam:若要更短的一行代码,请参见我的回答。 - John Machin
2
@John:对你的回答点个赞;仅利用“底层写入器实例”肯定比“费力的身份映射”更可取。 - mechanical_meat
1
@endolith:感谢您的反馈。将该部分移至答案顶部。 - mechanical_meat
1
由于您也在使用dictReader,因此可以轻松地使用dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames)添加字段。这样,如果您的字段发生更改,则无需调整dictWriter。 - Spencer Rathbun
显示剩余4条评论

31

有几个选项:

(1) 费力地将您的字段名称制作成一个身份映射字典(即不执行任何操作),以便csv.DictWriter可以将其转换回列表并将其传递给csv.writer实例。

(2) 文档提到了“底层writer实例”…所以只需使用它(示例在末尾)。

dw.writer.writerow(dw.fieldnames)

(3) 避免使用csv.Dictwriter,而是使用csv.writer自己编写代码

写入数据:

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

或者

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

我希望自己编写代码,而不是使用“extrasaction”功能;这样,您可以报告所有“extras”的键和值,而不仅仅是第一个额外的键。使用DictWriter的真正麻烦之处在于,如果您在构建每个字典时已经验证了键,则需要记住使用extrasaction ='ignore',否则它将慢慢(fieldnames是一个列表)重复检查。

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

目前在Python 3.6中,extrasaction功能似乎实现得更好了。现在是wrong_fields = rowdict.keys() - self.fieldnames,因此它实际上是一个set操作。 - martineau
我正在为“避免使用DictWriter”的评论投票支持 - 我没有看到使用它的任何优势,似乎构建数据并使用csv.writer更快。 - neophytte

8
另一种做法是,在输出行之前添加以下内容:

,然后再添加行。
output.writerow(dict(zip(dr.fieldnames, dr.fieldnames)))

zip函数将返回一个包含相同值的doublet列表。该列表可用于初始化字典。


4
writer.writeheader() 

以上方法非常有效。只需在初始化csv.DictWriter时不要忘记添加fieldnames属性即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接