【问题标题】:How to build a nested ordered dict from a csv?如何从 csv 构建嵌套的有序字典?
【发布时间】:2015-08-05 11:33:00
【问题描述】:

如何获得嵌套字典,其中键和子键精确与 csv 文件中的顺序相同?

我试过了

import csv
from collections import OrderedDict

filename = "test.csv"
aDict = OrderedDict()

with open(filename, 'r') as f:
    csvReader = csv.DictReader(f)
    for row in csvReader:
        key = row.pop("key")
        aDict[key] = row

test.csv 的样子

key,number,letter
eins,1,a
zwei,2,b
drei,3,c

但子词典没有排序(行字母和数字已更改)。那么如何以有序的方式填充aDict[key]

【问题讨论】:

    标签: python csv dictionary nested ordereddictionary


    【解决方案1】:

    您必须自己从csv.reader 返回的行中构建字典和子字典,这些行是序列,而不是使用csv.DictReader

    幸运的是,这相当容易:

    import csv
    from collections import OrderedDict
    
    filename = 'test.csv'
    aDict = OrderedDict()
    
    with open(filename, 'rb') as f:
        csvReader = csv.reader(f)
        fields = next(csvReader)
        for row in csvReader:
            temp = OrderedDict(zip(fields, row))
            key = temp.pop("key")
            aDict[key] = temp
    
    import json  # just to create output
    print(json.dumps(aDict, indent=4))
    

    输出:

    {
        "eins": {
            "number": "1",
            "letter": "a"
        },
        "zwei": {
            "number": "2",
            "letter": "b"
        },
        "drei": {
            "number": "3",
            "letter": "c"
        }
    }
    

    【讨论】:

    • 一个小问题:csv.OrderedDict 会跳过空行,csv.reader 不会。
    • @HaiVu:我假设你的意思是csv.DictReader 跳过空白行。虽然是真的,但模拟起来很简单——只需在for row in csvReader: 循环的开头添加一个if not row: continue
    • 是的。这就是我的意思,这是一个有效的解决方案。
    【解决方案2】:

    这是一种方式:

    import csv
    from collections import OrderedDict
    
    filename = "test.csv"
    aDict = OrderedDict()
    
    with open(filename, 'r') as f:
        order = next(csv.reader(f))[1:]
        f.seek(0)
    
        csvReader = csv.DictReader(f)
        for row in csvReader:
            key = row.pop("key")
            aDict[key] = OrderedDict((k, row[k]) for k in order)
    

    【讨论】:

    • 这看起来相当不错并且使用DictReader(),我喜欢。我不喜欢order = next(f).strip().split(',')[1:],因为有时我的 csv 可能是用分号分隔的。在我的脚本中,我使用嗅探器来确定方言,但为了简单起见,我在这里跳过了。
    • 您需要告诉DictReader() 也使用分号。所以嗅探,找到分隔符,回到文件开头,用找到的分隔符读取顺序,再次回到开头文件。
    • +1 为您的解决方案,但是,在计算 order 时,您不仅需要处理不同的分隔符(逗号、分号、制表符...),还需要处理引号(例如 “字段 1”、“字段 2”)。这就是为什么我们需要将其留给csv 模块来处理这些细节,而不是尝试自己拆分字段。
    • 建议:order = csvReader.fieldnames[1:]。在您创建 csvReader 之后立即放置
    【解决方案3】:

    csv.DictReader 将行加载到常规的dict 中,而不是有序的行中。您必须手动将 csv 读入 OrderedDict 以获得您需要的订单:

    from collections import OrderedDict
    
    filename = "test.csv"
    dictRows = []
    
    with open(filename, 'r') as f:
        rows = (line.strip().split(',') for line in f)
        # read column names from first row
        columns = rows.next()
        for row in rows:
            dictRows.append(OrderedDict(zip(columns, row)))
    

    【讨论】:

    • 如果字段包含嵌入式逗号,这可能不起作用。这就是我们需要使用csv 模块的原因。此外,并非所有 CSV 文件都使用逗号作为分隔符;相反,他们可能会使用制表符或分号。
    【解决方案4】:

    您可以利用现有的csv.DictReader 类,但更改它返回的行。为此,请将以下类添加到脚本的开头:

    class OrderedDictReader(csv.DictReader):
        def next(self):
            # Get a row using csv.DictReader
            row = csv.DictReader.next(self)
    
            # Create a new row using OrderedDict
            new_row = OrderedDict(((k, row[k]) for k in self.fieldnames))
            return new_row
    

    然后,用这个类代替csv.DictReader

    csvReader = OrderedDictReader(f)
    

    其余代码保持不变。

    【讨论】:

      猜你喜欢
      • 2021-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-05
      • 2018-05-05
      • 2017-08-08
      • 2017-07-25
      相关资源
      最近更新 更多