我在文件中有一个ASCII表,我想从中读取一组特定的行(例如,4003至4005行)。问题是该文件可能非常长(例如,十万到几百万行),我想尽快这样做。
错误的解决方案 :读取整个文件,然后转到这些行,
f = open('filename') lines = f.readlines()[4003:4005]
更好的解决方案 :enumerate遍历每行,以便不将其全部存储在内存中http://codingdict.com/questions/775
enumerate
f = open('filename') lines = [] for i, line in enumerate(f): if i >= 4003 and i <= 4005: lines.append(line) if i > 4005: break # @Wooble
最佳解决方案?
但这仍然需要遍历每一行。是否有更好的(从速度/效率方面)访问特定线路的方法?即使我只会访问一次文件(通常),也应该使用线缓存吗?
使用二进制文件代替,在这种情况下,跳过可能更容易,这是一个选项—但我宁愿避免使用它。
我可能只会使用itertools.islice。在像文件句柄这样的可迭代对象上使用islice意味着永远不会将整个文件读入内存,并且尽可能快地丢弃前4002行。您甚至可以非常便宜地将所需的两行转换为列表(假设这两行本身不太长)。然后,您可以退出该with块,关闭文件句柄。
itertools.islice
with
from itertools import islice with open('afile') as f: lines = list(islice(f, 4003, 4005)) do_something_with(lines)
但是,圣牛的行缓存速度更快,可以进行多次访问。我创建了一个百万行的文件来比较islice和linecache,然后将其删除。
>>> timeit("x=islice(open('afile'), 4003, 4005); print next(x) + next(x)", 'from itertools import islice', number=1) 4003 4004 0.00028586387634277344 >>> timeit("print getline('afile', 4003) + getline('afile', 4004)", 'from linecache import getline', number=1) 4002 4003 2.193450927734375e-05 >>> timeit("getline('afile', 4003) + getline('afile', 4004)", 'from linecache import getline', number=10**5) 0.14125394821166992 >>> timeit("''.join(islice(open('afile'), 4003, 4005))", 'from itertools import islice', number=10**5) 14.732316970825195
这不是一个实际的测试,但是即使在每个步骤中重新导入线缓存,也只比islice慢一秒钟。
>>> timeit("from linecache import getline; getline('afile', 4003) + getline('afile', 4004)", number=10**5) 15.613967180252075
是的,对于所有缓存,linecache的速度都比islice快,但它会不断地重新创建linecache,但是谁来做呢?对于可能的情况(一次仅读取几行,一次读取多行),行缓存速度更快,并且呈现简洁的语法,但是该islice语法也非常干净快捷,并且永远不会将整个文件读入内存。在RAM密集型环境中,islice解决方案可能是正确的选择。对于非常高的速度要求,行缓存可能是更好的选择。但是实际上,在大多数环境中,两次时间都足够小,几乎没有关系。
islice