繁体   English   中英

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

[英]Python csv.DictReader - how to reverse output?

我试图扭转文件的读取方式。 我正在使用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'}

您必须将整个 CSV文件读入内存; 你可以通过调用reader对象上的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'}

读到列表并反转:

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)):

您实际上不必将整个文件读入内存。

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 in以终止进程并等待几分钟才能恢复......)。

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

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


*正如Martijn Pieters在评论中指出的那样,如果你没有使用显式fieldnamesDictReader需要一个可迭代的字符串,其中第一行是标题 但是你可以通过分别用csv.reader读取第一行并将其作为fieldnames传递来解决这个问题,甚至可以通过itertools.chain来解决这个问题。来自前向读取的所有第一行除了后向读取的最后csv.reader行之外。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM