Dil*_*rix 16 python io performance file
我在一个文件中有一个ASCII表,我想从中读取一组特定的行(例如,行4003到4005).问题是这个文件可能非常长(例如,数千到数百万行),我想尽快做到这一点.
糟糕的解决方案:读入整个文件,然后转到这些行,
f = open('filename')
lines = f.readlines()[4003:4005]
Run Code Online (Sandbox Code Playgroud)
更好的解决方案:enumerate在每条线路上,以便它不在内存中(la /sf/answers/145731631/)
f = open('filename')
lines = []
for i, line in enumerate(f):
if i >= 4003 and i <= 4005: lines.append(line)
if i > 4005: break # @Wooble
Run Code Online (Sandbox Code Playgroud)
最佳方案?
但这仍然需要通过每一行.是否有更好的(在速度/效率方面)访问特定线路的方法?即使我只访问文件一次(通常),我应该使用linecache吗?
使用二进制文件,在这种情况下可能更容易跳过,是一个选项---但我宁愿避免它.
koj*_*iro 18
我可能只是用itertools.islice.在像文件句柄这样的迭代上使用islice意味着整个文件永远不会被读入内存,并且尽可能快地丢弃前4002行.您甚至可以非常便宜地将您需要的两条线投射到一个列表中(假设线条本身不是很长).然后你可以退出with块,关闭文件句柄.
from itertools import islice
with open('afile') as f:
lines = list(islice(f, 4003, 4005))
do_something_with(lines)
Run Code Online (Sandbox Code Playgroud)
但是,对于多次访问,神圣的牛是更快的线路缓存.我创建了一个百万行文件来比较islice和linecache以及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
Run Code Online (Sandbox Code Playgroud)
这不是一个实际测试,但即使在每一步重新导入linecache,它只比islice慢一秒.
>>> timeit("from linecache import getline; getline('afile', 4003) + getline('afile', 4004)", number=10**5)
15.613967180252075
Run Code Online (Sandbox Code Playgroud)
是的,linecache比islice快,但不断重新创建linecache,但是谁做到了?对于可能的场景(只读几行,一次读取多行,一次),linecache更快并且呈现简洁的语法,但islice语法也非常干净和快速,并且不会将整个文件读入内存.在RAM紧密的环境中,islice解决方案可能是正确的选择.对于非常高的速度要求,linecache可能是更好的选择.但实际上,在大多数环境中,两次都足够小,几乎无关紧要.
这里的主要问题是,换行符与任何其他角色没有任何不同.因此操作系统无法跳到该行.
也就是说,有一些选择,但每一个你必须以某种方式做出牺牲.
您确实已经说明了第一个:使用二进制文件.如果你有固定的行长,那么你可以seek提前line * bytes_per_line字节并直接跳转到该行.
下一个选项是使用索引:创建第二个文件,并在此索引文件的每一行中写入数据文件中该行的字节索引.现在访问数据文件涉及两个搜索操作(跳到line索引,然后跳到index_value数据文件中),但它仍然会非常快.另外:将节省磁盘空间,因为线路可以有不同的长度.减:您无法使用编辑器触摸数据文件.
还有一个选择:(我想我会这样做)是只使用一个文件但是每行都有行号和某种分隔符.(例如4005:我的数据线).现在,您可以使用二进制搜索的修改版本https://en.wikipedia.org/wiki/Binary_search_algorithm来搜索您的行.这将采用log(n)搜索操作,其中n是总行数.另外:您可以编辑文件,与固定长度的线相比可以节省空间.它仍然非常快.即使是一百万行,这也只是大约20次即时发生的搜寻行动.减:这些可能性中最复杂的.(但有趣的事情;)
编辑:另一个解决方案:在许多smaler文件中拆分您的文件.如果你有很长的'线',这可能会小到每个文件一行.但后来我会将它们分组放在文件夹中,例如4/0/05.但即使用较短的线条划分你的文件 - 大概 - 大概 - 1mb块,命名为1000.txt,2000.txt并读取与你的线完全匹配的一个(或两个)应该很快就很容易实现.
| 归档时间: |
|
| 查看次数: |
8682 次 |
| 最近记录: |