【问题标题】:How to write only a subset of fields using DictReader and DictWriter如何使用 DictReader 和 DictWriter 仅写入字段的子集
【发布时间】:2017-04-29 14:51:58
【问题描述】:

名为 Sample.csv 的 CSV 测试文件包含:

Brand, Price, Weight, Type
brand1, 6.05, 3.2, orange
brand2, 8.05, 5.2, orange
brand3, 6.54, 4.2, orange
brand1, 6.05, 3.2, pear
brand2, 7.05, 3.6, pear
brand3, 7.45, 3.9, pear
brand1, 5.45, 2.7, apple
brand2, 6.05, 3.2, apple
brand3, 6.43, 3.5, apple
brand4, 7.05, 3.9, apple
brand1, 8.05, 4.2, plum
brand2, 3.05, 2.2, plum

我的代码是:

import csv
headers = ['Brand','Price','Type']

with open('sample.csv', newline='') as rf:
    reader = csv.DictReader(rf, delimiter=',',fieldnames=headers)
    with open('output.csv', 'w', newline='') as wf:
        writer = csv.DictWriter(wf, delimiter=',', extrasaction='ignore', fieldnames=headers)
        writer.writerow(dict((fn,fn) for fn in writer.fieldnames))
        for row in reader:
           print(row)
           writer.writerow(row)

我只是想将品牌、价格、类型输入输出文件,但我得到:

Brand,Price,Type
Brand, Price, Weight
brand1, 6.05, 3.2
brand2, 8.05, 5.2
brand3, 6.54, 4.2
brand1, 6.05, 3.2
brand2, 7.05, 3.6
brand3, 7.45, 3.9
brand1, 5.45, 2.7
brand2, 6.05, 3.2
brand3, 6.43, 3.5
brand4, 7.05, 3.9
brand1, 8.05, 4.2
brand2, 3.05, 2.2

为什么我在输出中得到的是权重字段,而不是类型字段?

请注意,添加了行 writer.writerow(dict((fn,fn) for fn in writer.fieldnames)) 用于调试,有意打印出标题两次。

【问题讨论】:

  • 如果我们看到输入文件的一小部分样本,我们将更有能力提供帮助——小到足以重现问题而不会产生过多的负担。
  • 输入文件,sample.csv显示在顶部。

标签: python csv


【解决方案1】:

您在headers 中输入列名Brand, Price, Weight 的事实不会使DictReader 解析并仅从源文件中返回这些列。它只会将在文件中找到的列分配给这些键 - 实际上,您可以将它们命名为 First, Second, Third,结果将是相同的。

你有两个选择:

使用源标题行

利用你有一个标题行的事实,让DictReader根据它命名键,然后只写你想要的:

import csv
headers = ['Brand', 'Price', 'Type']

with open('sample.csv') as rf:
    reader = csv.DictReader(rf, delimiter=',', skipinitialspace=True)
    with open('output.csv', 'w') as wf:
        writer = csv.DictWriter(wf, delimiter=',', extrasaction='ignore', fieldnames=headers)
        writer.writeheader()
        for row in reader:
            print(row)
            writer.writerow(row)

显式设置列(在源和输出中)

明确源中的列是什么,并在输出中明确您想要的内容 - 使用两个列表:

import csv
present_headers = ['Brand', 'Price', 'Weight', 'Type']
desired_headers = ['Brand', 'Price', 'Type']

with open('sample.csv') as rf:
    reader = csv.DictReader(rf, delimiter=',', fieldnames=present_headers, skipinitialspace=True)
    with open('output.csv', 'w') as wf:
        writer = csv.DictWriter(wf, delimiter=', ', extrasaction='ignore', fieldnames=desired_headers)
        writer.writeheader()
        next(reader, None)   #to skip writing the header row from the source
        for row in reader:
            # print(row)
            writer.writerow(row)

鉴于您问题中的示例,这两个选项都会产生相同的结果。

几句话 - 问题中的示例在标题和数据行中都有分隔符 , 后跟空格。如果它确实存在(不是复制和粘贴错误),阅读器中的选项skipinitialspace=True 将删除它 - 否则它会成为列中数据的一部分。

另一个是不需要手动构造 DictWriter 的标题行 - 这就是 writerheader() 方法的用途。

最后,您可以在选项 2 中看到在写入数据之前调用 next(reader, None) - 它的目的是让读者产生第一行,因为它现在只是一个普通(而不是标题)行,我们不想在输出中复制它。

【讨论】:

    【解决方案2】:

    使用 Python 3.5.2,我能够完全复制您的问题,所以我假设这也是您的版本。

    您的问题是 DictReader 对象需要匹配 sample.csv 中的标题,否则 reader 将不包含正确的数据开头。因为len(headers)sample.csv 中的列数不匹配,DictReader 将第一个len(headers) 列映射到您的reader 键,这就是为什么“类型”和“重量”在您的output.csv。虽然您可以在代码中复制 sample.csv 标头并分别区分 DictReader 和 DictWriter 的输入和输出标头,但我建议让 CSV 处理正确的标头映射。只需从 DictReader 实例中删除 fieldnames=headers 即可完成此操作。

    现在您让 DictReader 使用 sample.csv 标头作为键,您需要将用于调试的线路作为永久固定装置(尽管内置的 writer.writeheader() 是一个更简洁的变体)。另外,因为sample.csv 在每个值之前都包含空格,所以除了第一个元素之外的每个header 元素都需要有一个前导空格来匹配reader 中的键。

    您的最终代码可能如下所示:

    import csv
    headers = ['Brand',' Price',' Type']
    
    with open('sample.csv', newline='') as rf:
        reader = csv.DictReader(rf, delimiter=',')
        with open('output.csv', 'w', newline='') as wf:
            writer = csv.DictWriter(wf, delimiter=',', extrasaction='ignore', fieldnames=headers)
            writer.writeheader()
            for row in reader:
               print(row)     
               writer.writerow(row)
    

    【讨论】:

    • @Todor 中提到的skipinitialspace=True 标志很神奇。那么headers 元素中不再需要空格。
    • 谢谢——这也很有帮助
    猜你喜欢
    • 2011-03-13
    • 2014-06-02
    • 2020-05-21
    • 1970-01-01
    • 2015-01-02
    • 2013-02-11
    • 2016-05-13
    • 2013-06-07
    • 2016-05-31
    相关资源
    最近更新 更多