我正在处理一个相对较大的文件(大约2GB)。它的内容在一段时间内不断需要,运行至少1-2天。
有足够的RAM,我在循环之前将整个文件加载到内存中,使用:
f = open(filename)
lines = f.readlines()
while ...
#using different portions of the file (randomly picked)
我想知道如果程序要长时间运行,我是否会面临内存管理问题。具有完整内容的文件是否会在内存中保持不变,但可能需要多长时间?如果没有,我有哪些替代方案?
当然我最初尝试做的事情是正确的,只需要读取循环的每次迭代所需的部分,使用itertools中的islice,并使用seek(0)将迭代器设置回0用于随后的循环运行。但它运行得非常慢,因为文件很大而while循环很长。
评论后更多澄清:
当我没有将它加载到内存中时,我基本上是这样做的:
from itertools import islice
f = open(filename)
while ...:
for line in islice(f, start_line, end_line):
text += line
f.seek(0)
与我在内存中加载的内容相比,它真的很慢,如下所示:
lines = f.readlines()
while...:
for i in range(start_line, end_line): text += lines[i]
答案 0 :(得分:1)
您在内存中保留的数据类型是一个列表,而不是文件对象,因此Python将特别小心,不要在以后使用该列表时对其进行垃圾收集。
如果你没有按顺序使用它并不重要。 Python在编译之前会对代码进行分析,并且他知道稍后您将使用此列表。
无论如何,如果你在文件对象上使用seek()和tell(),我就不明白它为什么会变慢。
除非你的线条像大象一样大。
Seek将读/写指针移动到您希望的内存块(文件内)。当你以后执行f.readline()时,它会直接跳转到那里。
不应该慢。如果你使用它,你将避免一些其他程序崩溃的可能性,因为Python保留了大量的内存。
此外,Python列表并非完全不确定。我认为它可以在32位PC上容纳更多10 ** 7项。
所以你有多少行也很重要。
直接从HD / SSD / Flash快速随机读取的示例:
from random import randint
from time import sleep
f = open("2GB.file", "rb")
linemap = [] # Keeps the start and end position of each line
for x in f:
linemap.append((f.tell(), len(x)))
# It is slightly faster to have start and length than only start and then f.readline()
# But either way will work OK for you
def getline (index):
line = linemap[index]
f.seek(line[0])
return f.read(line[1])
def getslice (start=0, stop=None):
if stop==None: stop = len(linemap)
howmany = 0
for x in xrange(start, stop): howmany += linemap[x][1]
f.seek(linemap[start][0])
return f.read(howmany).splitlines(1)
while True:
print getline(randint(0, len(linemap)-1))
sleep(2)
当然,速度永远不能与RAM的直接访问相匹配。只是为了清楚。但是,与使用islice()的解决方案相比,这很快。虽然你实际上可以使用islice()以相同的速度做同样的事情,但你必须寻找甚至那时代码会变得有点混乱。
答案 1 :(得分:1)
根据我的评论解释,您可以创建一个函数来返回内存中的字节缓冲区,并缓存该函数以对仅仅变量进行更多控制。
例如(如果您使用的是python3.2 +,3.3 +使用"键入"选项):
from functools import lru_cache
import io
@lru_cache(maxsize=None, typed=True) # typed will cache as per different arg.
def get_cached_file(filename):
m = io.BytesIO()
with open(filename, 'rb') as f:
m.write(f.read())
return m
用法:
a = get_cached_file('a.file')
b = get_cached_file('b.file')
# since the files are new to cache, they belong "misses"
get_cached_file.cache_info()
CacheInfo(hits=0, misses=2, maxsize=None, currsize=2)
a1 = get_cached_file('a.file')
b2 = get_cached_file('b.file')
# simply return the result from cache, ie. "hits"
get_cached_file.cache_info()
CacheInfo(hits=2, misses=2, maxsize=None, currsize=2)
要阅读缓冲区,您只需要seek(0)
或任何你想要的东西。
您也可以清除缓存:
get_cached_file.cache_clear()
# now its counter reset to "0"
get_cached_file.cache_info()
CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)
您可以阅读更多here
如果您使用的是python2.x,请注意一些现有的库,以便在内存中进行缓存,例如memcached或redis。您当然也可以实现自己的缓存。
希望这有帮助。