Python csv.DictReader - 如何反转输出?

时间:2014-07-29 17:33:57

标签: python csv dictionary reverse

我试图改变文件的读取方式。我正在使用DictReader,因为我想要一个字典中的内容。我想读取文件中的第一行并将其用于Keys,然后反向解析文件(从下到上),类似于linux“tac”命令。是否有捷径可寻?下面是我将代码读入字典并将其写入文件的代码......

reader = csv.DictReader(open(file_to_parse, 'r'), delimiter=',', quotechar='"')
for line in reader:
    # ...

此代码可以正常处理文件,但是..我需要它来从最后读取文件。

换句话说,我希望它能阅读文件:

fruit, vegetables, cars
orange, carrot, ford
apple, celery, chevy
grape, corn, chrysler

并且能够让它返回:

{' cars': ' chrysler', ' vegetables': ' corn', 'fruit': 'grape'}
{' cars': ' chevy', ' vegetables': ' celery', 'fruit': 'apple'}
{' cars': ' ford', ' vegetables': ' carrot', 'fruit': 'orange'}

而不是:

{' cars': ' ford', ' vegetables': ' carrot', 'fruit': 'orange'}
{' cars': ' chevy', ' vegetables': ' celery', 'fruit': 'apple'}
{' cars': ' chrysler', ' vegetables': ' corn', 'fruit': 'grape'}

3 个答案:

答案 0 :(得分:2)

您必须将整个 CSV文件读入内存;您可以通过在阅读器对象上调用list()来执行此操作:

with open(file_to_parse, 'rb') as inf:
    reader = csv.DictReader(inf, skipinitialspace=True)
    rows = list(reader)

for row in reversed(rows):

请注意,我在此处使用该文件作为上下文管理器来确保文件已关闭。您还希望以二进制模式打开文件(将换行处理留给csv模块)。您传递给DictReader()的其余配置是默认值,因此我省略了它们。

我将skipinitialspace设置为True,从您的示例输入和输出判断,您的分隔符后面有空格;该选项删除了这些。

csv.DictReader()对象负责将第一行作为键读取。

演示:

>>> import csv
>>> sample = '''\
... fruit, vegetables, cars
... orange, carrot, ford
... apple, celery, chevy
... grape, corn, chrysler
... '''.splitlines()
>>> reader = csv.DictReader(sample, skipinitialspace=True)
>>> rows = list(reader)
>>> for row in reversed(rows):
...     print row
... 
{'cars': 'chrysler', 'vegetables': 'corn', 'fruit': 'grape'}
{'cars': 'chevy', 'vegetables': 'celery', 'fruit': 'apple'}
{'cars': 'ford', 'vegetables': 'carrot', 'fruit': 'orange'}

答案 1 :(得分:1)

读到列表并反转:

lines = [x for x in reader]
for line in lines[::-1]:
    print line

{' cars': ' chrysler', ' vegetables': ' corn', 'fruit': 'grape'}
{' cars': ' chevy', ' vegetables': ' celery', 'fruit': 'apple'}
{' cars': ' ford', ' vegetables': ' carrot', 'fruit': 'orange'}

或者Martijn Pieters建议:

for line in reversed(list(reader)):

答案 2 :(得分:0)

您实际上 没有将整个文件读入内存。

csv.DictReader实际上并不需要文件,只是一个可迭代的字符串。*

你可以在平均线性时间内以相反的顺序读取一个文本文件,其中空间不是太多。这不是微不足道的,但并不是那么难:

def reverse_lines(*args, **kwargs):
    with open(*args, **kwargs) as f:
        buf = ''
        f.seek(0, io.SEEK_END)
        while f.tell():
            try:
                f.seek(-1024, io.SEEK_CUR)
            except OSError:
                bufsize = f.tell()
                f.seek(0, io.SEEK_SET)
                newbuf = f.read(bufsize)
                f.seek(0, io.SEEK_SET)
            else:
                newbuf = f.read(1024)
                f.seek(-1024, io.SEEK_CUR)
            buf = newbuf + buf
            lines = buf.split('\n')
            buf = lines.pop(0)
            yield from reversed(lines)
        yield buf

这没有经过严格的测试,它剥离了换行符(这对于csv.DictReader来说很好,但一般来说不是很好),并且它没有针对不寻常但可能的边缘情况进行优化(例如,真的很长线,它将是二次的),它需要Python 3.3,并且文件不会消失,直到你关闭/释放迭代器(它可能应该是一个上下文管理器,所以你可以处理它) - 但如果你真的想要这个,我愿意打赌你可以在ActiveState上找到一个食谱,或者在PyPI上找到一个没有这些问题的分发。

无论如何,对于一个中等大小的文件,我怀疑在几乎任何现实生活中的文件系统中,它实际上要以正向顺序将整个内容读入内存然后反向迭代列表。但对于一个非常大的文件(特别是一个你甚至无法适应内存的文件),这个解决方案显然要好得多。

通过快速测试(请参阅http://pastebin.com/Nst6WFwV代码),在我的计算机上,基本的细分是:

  • 文件速度慢很多<<<<<<<<< 1000行。
  • 从1K-1M线路减慢约10%。
  • 交叉约30M线。
  • 500M线路快50%。
  • 在1.5G线路上快了1300%。
  • 在2.5G线路上有效地无限快速(列表反转版本将我的机器投入交换地狱,我必须ssh进入终止进程并等待几分钟才能恢复......)。

当然,细节将取决于有关您计算机的大量事实。可能并非巧合的是,500M 72-char的ASCII线占据了我机器上近一半的物理RAM。但是使用硬盘而不是SSD你可能会看到reverse_lines的更多惩罚(因为随机搜索与连续读取相比要慢得多,而且一般来说磁盘会更重要)。而你的平台的malloc和VM行为,甚至地点问题(在读取它之后几乎立即解析一条线而不是在它被换出并重新进入......之后)可能会有所不同。等等。

无论如何,经验教训是,如果你不期望至少有数百万行(或者在资源有限的机器上可能少一点),甚至不要考虑这个问题;保持简单。


*正如Martijn Pieters在评论中指出的那样,如果你没有使用明确的fieldnamesDictReader需要一个可迭代的字符串,其中第一行是标题< / em>的。但是你可以通过单独用csv.reader读取第一行并将其作为fieldnames传递,或者甚至通过itertools.chain来解决这一问题,然后将所有第一行从正向读取之前移除向后阅读的最后几行。