Python 在多个标题行上拆分csv中的行

Python 在多个标题行上拆分csv中的行,python,csv,dictionary,Python,Csv,Dictionary,我对python很陌生,所以请温柔一点 我有一个.csv文件,以这种格式向我报告,因此我对此无能为力: ClientAccountID AccountAlias CurrencyPrimary FromDate SomeID SomeAlias SomeCurr SomeDate OtherID OtherAlias OtherCurr OtherDate ClientAccountID

我对python很陌生,所以请温柔一点

我有一个.csv文件,以这种格式向我报告,因此我对此无能为力:

ClientAccountID   AccountAlias   CurrencyPrimary    FromDate
         SomeID      SomeAlias          SomeCurr    SomeDate
        OtherID     OtherAlias         OtherCurr   OtherDate
ClientAccountID   AccountAlias   CurrencyPrimary    AssetClass
         SomeID      SomeAlias          SomeCurr     SomeClass
        OtherID     OtherAlias         OtherCurr     OtherDate
      AnotherID   AnotherAlias       AnotherCurr   AnotherDate
我正在使用python中的csv包,因此我有:

with open(theFile, 'rb') as csvfile:
    theReader = csv.DictReader(csvfile, delimiter = ',')
据我所知,它创造了字典“theReader”。如何将此词典子集为多个词典,并按原始csv文件中的标题行进行拆分?是否有一种简单、优雅、非循环的方法来创建字典列表(甚至是一个字典字典,使用帐户ID作为键)?这有意义吗

哦。请注意,标题行并不相等,但标题行将始终以“ClientAccounted”开头

多亏了@codie,我现在基于使用“\t”分隔符,使用以下命令将csv拆分为几个dict

with open(theFile, 'rb') as csvfile:
    theReader = csv.DictReader(csvfile, delimiter = '\t')
但是,我现在将整个标题行作为键,将其他行作为值。我该如何进一步拆分

感谢下面的@Benjamin Hodgson,我有以下几点:

from csv import DictReader
from io import BytesIO

stringios = []

with open('file.csv', 'r') as f:
    stringio = None
    for line in f:
        if line.startswith('ClientAccountID'):
            if stringio is not None:
                stringios.append(stringio)
            stringio = BytesIO()
        stringio.write(line)
        stringio.write("\n")
    stringios.append(stringio)

data = [list(DictReader(x.getvalue(), delimiter=',')) for x in stringios]
如果我在stringios中打印第一个项目,我会得到预期的结果。它看起来像一个单一的csv。但是,如果我使用下面的方法打印数据中的第一项,我会得到一些奇怪的结果:

for row in data[0]:
    print row
它返回:

{'C':'U'}
{'C':'S'}
{'C':'D'}
...

因此,它似乎在拆分每个字符,而不是使用逗号分隔符。

如果我正确理解了您的问题,您有一个包含多个表的CSV文件。表格由标题行分隔,标题行始终以字符串
“clientaccounted”
开头

因此,任务是将CSV文件读入字典列表。列表中的每个条目都对应于CSV文件中的一个表

我是这样做的:

  • 将包含多个表的单个CSV文件分解为多个文件,每个文件包含一个表。(这些文件可能在内存中。)通过查找以
    “clientAccount”
    开头的行来执行此操作
  • 使用
    DictReader
    将这些文件读入词典列表
  • 下面是一些代码,可以将文件读入s的列表。(A
    StringIO
    是内存中的文件。它通过将字符串包装到类似文件的接口来工作)

    如果遇到以
    'ClientAccountID'
    开头的行,我们将当前的
    StringIO
    放入列表,并开始写入新的行。完成后,记得将最后一个也添加到列表中。 在使用
    StringIO.seek(0)
    写入
    StringIO后,不要忘记(正如我在本答案的早期版本中所做的那样)回放
    StringIO

    现在可以直接循环
    StringIO
    s以获得字典表

    data = [list(DictReader(x, delimiter='\t')) for x in stringios]
    
    对于列表
    stringios
    中的每个类似文件的对象,创建一个
    DictReader
    并将其读入列表


    如果您的数据太大,无法放入内存,那么修改这种方法并不难。使用生成器而不是列表,并逐行处理。

    如果数据不是逗号或制表符分隔的,则可以使用
    str.split
    ,您可以将其与
    itertools.groupby
    组合以分隔标题和行:

    from itertools import groupby, izip, imap
    
    with open("test.txt") as f:
        grps, data = groupby(imap(str.split, f), lambda x: x[0] == "ClientAccountID"), []
        for k, v in grps:
            if k:
                names = next(v)
                vals = izip(*next(grps)[1])
                data.append(dict(izip(names, vals)))
    
    from pprint import pprint as pp
    
    pp(data)
    
    输出:

    [{'AccountAlias': ('SomeAlias', 'OtherAlias'),
      'ClientAccountID': ('SomeID', 'OtherID'),
      'CurrencyPrimary': ('SomeCurr', 'OtherCurr'),
      'FromDate': ('SomeDate', 'OtherDate')},
     {'AccountAlias': ('SomeAlias', 'OtherAlias', 'AnotherAlias'),
      'AssetClass': ('SomeClass', 'OtherDate', 'AnotherDate'),
      'ClientAccountID': ('SomeID', 'OtherID', 'AnotherID'),
      'CurrencyPrimary': ('SomeCurr', 'OtherCurr', 'AnotherCurr')}]
    
    如果以制表符分隔,只需更改一行:

    with open("test.txt") as f:
        grps, data = groupby(csv.reader(f, delimiter="\t"), lambda x: x[0] == "ClientAccountID"), []
        for k, v in grps:
            if k:
                names = next(v)
                vals = izip(*next(grps)[1])
                data.append(dict(izip(names, vals)))
    

    使用制表符分隔符,
    (\t)
    而不是逗号分隔符。多简单啊。然后如何创建子字典?
    用于reader:do something()
    中的行,其中行是给定行中的值的dict。Python为您提供了所有隐藏的魔力。您需要展示csv文件中的一些实际行,以便我们能够给您一个正确的答案。如果您有一个csv文件,其中有三行(或七行)的块中有多个不同的id,那么在读取该文件时,您需要做一些魔术。目前的例子是模棱两可的…您需要编造一些虚假数据,以便更好地了解格式。取前10行或其他内容并混淆数据。我得到一个错误:stringio.write(line)TypeError:unicode参数应为'str',是的,您完全理解我的意图。谢谢。这个答案是Python 3代码。根据那个错误信息,我猜你是在2.7上。尝试将
    StringIO
    更改为
    BytesIO
    。是的,我使用的是2.7。这些信息可能与。。。道歉。仍然会遇到问题,因为它似乎在工作,但“数据”在完成时是空的。我正在处理它。建议没有任何以
    'clientaccount'
    开头的行。行的开头有空格吗?如果是这样,请尝试
    line.strip().startswith('ClientAccountID')
    而不是
    line.startswith(…)
    。(另外,我刚刚发现并修复了答案中的一个bug;)抱歉-刚刚更新。似乎无法识别分隔符?或者是在每个字符处拆分,而不是在逗号处。这很好。我不知道lambda这个东西,但它非常有用。另外,你能不能在第二秒也使用str.split?是否有方法向其传递不同的分隔符(\t)?另外,查看imap的文档,它表示imap(函数,*iterables),这意味着文件f是可写的?我是python新手,我只是好奇,但是当传递一个文件时,它会被解释为一组iterable行吗?如果是这样,您迭代这些行,str.split它们,然后按第一个术语是clientAccounted的行对它们进行分组?我想我理解对了,但似乎。。。不寻常(?)文件只是一组可编辑的行?或者这(相当)正常?谢谢你@lukehawk,您可以传递任何您想要的分隔符,但是在map示例中,您需要使用
    map(lambda x:x.split(“whatever”)
    ,我们可以传递
    str.split
    ,因为它是可调用的,所以我们不需要lambda,是的,一个文件对象返回它自己的迭代器,这样当你迭代它时,你每次都会得到一行,imap在任何空格上拆分每一行,然后每次我们
    如果k
    为真,我们就有一行带有f
    with open("test.txt") as f:
        grps, data = groupby(csv.reader(f, delimiter="\t"), lambda x: x[0] == "ClientAccountID"), []
        for k, v in grps:
            if k:
                names = next(v)
                vals = izip(*next(grps)[1])
                data.append(dict(izip(names, vals)))