我试图改变文件的读取方式。我正在使用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'}
答案 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代码),在我的计算机上,基本的细分是:
当然,细节将取决于有关您计算机的大量事实。可能并非巧合的是,500M 72-char的ASCII线占据了我机器上近一半的物理RAM。但是使用硬盘而不是SSD你可能会看到reverse_lines
的更多惩罚(因为随机搜索与连续读取相比要慢得多,而且一般来说磁盘会更重要)。而你的平台的malloc和VM行为,甚至地点问题(在读取它之后几乎立即解析一条线而不是在它被换出并重新进入......之后)可能会有所不同。等等。
无论如何,经验教训是,如果你不期望至少有数百万行(或者在资源有限的机器上可能少一点),甚至不要考虑这个问题;保持简单。
*正如Martijn Pieters在评论中指出的那样,如果你没有使用明确的fieldnames
,DictReader
需要一个可迭代的字符串,其中第一行是标题< / em>的。但是你可以通过单独用csv.reader
读取第一行并将其作为fieldnames
传递,或者甚至通过itertools.chain
来解决这一问题,然后将所有第一行从正向读取之前移除向后阅读的最后几行。