Как написать строку заголовка с помощью csv.DictWriter?

114

Предположим, у меня есть 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)

Но как я могу включить имена полей?

Мартино
источник

Ответы:

149

Изменить:
в 2.7 / 3.2 есть новый writeheader()метод . Кроме того, ответ Джона Мачина предоставляет более простой метод записи строки заголовка.
Простой пример использования writeheader()метода, теперь доступного в 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

Для создания экземпляра 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)
mechanical_meat
источник
12
+1 Еще один способ написать заголовок: dw.writerow( dict((f,f) for f in dr.fieldnames) ).
FMc 05
2
@ Адам: для более короткого однострочного сообщения см. Мой ответ.
Джон Мачин
2
@John: +1 к вашему ответу; простое использование «базового экземпляра писателя», безусловно, предпочтительнее «трудоемкого картирования идентичности».
Mechanical_meat
1
@endolith: спасибо за отзыв. Эта часть перенесена в начало ответа.
Mechanical_meat
1
Поскольку вы также используете dictReader, легко добавлять поля с помощью dw = csv.DictWriter(fou, delimiter='\t', fieldnames=dr.fieldnames). Таким образом, если ваши поля изменяются, вам не нужно настраивать dictWriter.
Спенсер Ратбун,
29

Несколько вариантов:

(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«функциональности» я бы предпочел сам кодировать; таким образом вы можете сообщить обо ВСЕХ «дополнительных» ключах и значениях, а не только о первом дополнительном ключе. Что действительно неприятно с DictWriter, так это то, что если вы сами проверяли ключи при создании каждого dict, вам нужно не забыть использовать extrasaction = 'ignore', иначе он будет МЕДЛЕННО (имена полей - это список) повторить проверку:

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 so it's effectively a готово.
Мартино
Я голосую за этот ответ за комментарий «Избегайте DictWriter» - я не видел никаких преимуществ в его использовании и, кажется, быстрее структурирую ваши данные и использую csv.writer
neophytte
8

Другой способ сделать это - добавить перед добавлением строк в ваш вывод следующую строку:

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

Почтовый индекс вернет список дублетов, содержащих то же значение. Этот список можно использовать для запуска словаря.

Рафаэль Пр
источник