用于动态标头的Python CSV编写器

时间:2017-11-08 10:21:40

标签: python csv

我有很多字典在密钥方面不一致,我必须把它们写成CSV。

使用DictWriter时,需要在声明中定义我不知道的标题。我也无法将它们存储在列表中,直到我将所有dict计为dict计入数百万。

我正在寻找库,我可以继续用不一致的行写CSV,并在引擎盖下管理它们。

数据:

{'a':'1','i':'1','l':None}
{'b':'1','k':'1','y':None}
{'g':'1','k':'1','j':None}
{'b':'1','h':'1','c':None}
{'b':'1','h':'1','n':None}
{'a':'1','b':'1','v':None}
{'a':'1','b':'1','c':None}

预期输出:(像这样的CSV)

a   c   b   g   i   h   k   j   l   n   v   y
1               1                           
        1               1                   
            1           1                   
        1           1                       
        1           1                       
1       1                                   
1       1                                   

这个数据位于顶部,来自迭代器,无法将其存储在内存中,因为它非常大。

1 个答案:

答案 0 :(得分:1)

如果您不知道所有标题字段,并且您认为迭代可迭代的dicts以收集标题字段是低效的,那么您不应该使用DictWriter

您可以跟踪所有看到的字段,将它们写在每个字典中,并在每个新行的末尾添加新字段;以便较新的字段位于文件中行的尾部。在新行中找不到的现有字段将写为空白。

这是Python 3中的玩具代码,演示了这个概念:

import io
import csv

# supposedly lengthy iterator containing dicts
it = iter([{'name': 'Bob', 'house': 5, 'cell': 8090}, 
           {'name': 'Lisa', 'class': 12, 'age': 53}, 
           {'done': False, 'flat': 6}])
# simulate file
s = io.StringIO()
writer = csv.writer(s)

header, header_set = [], set()
writer.writerow('') # place holder for header
for row in it:
    for key in row:
       if key not in header_set:
           header_set.add(key)
           header.append(key)
    writer.writerow(row.get(col, '') for col in header)


# TEST: recover written file as iterable of dicts using DictReader
s.seek(0)
reader = csv.DictReader(s, fieldnames=header, restval='')
for row in reader:
    print(row)
OrderedDict([('name', 'Bob'), ('house', '5'), ('cell', '8090'), ('class', ''), ('age', ''), ('done', ''), ('flat', '')])
OrderedDict([('name', 'Lisa'), ('house', ''), ('cell', ''), ('class', '12'), ('age', '53'), ('done', ''), ('flat', '')])
OrderedDict([('name', ''), ('house', ''), ('cell', ''), ('class', ''), ('age', ''), ('done', 'False'), ('flat', '6')])

如果需要使用标题更新现有文件,可以将标题写入新文件,并将前一个文件中的所有行写入连续行:

new_file.write(','.join(header)+s.read())

但是,考虑到您最初声称行数非常大,可以通过使用文件编辑器手动将标头复制到现有文件中来避免内存密集型file.read:)