假设我有一个非常大的文件foo.txt
,我希望在找到正则表达式时迭代它。目前我这样做:
f = open('foo.txt')
s = f.read()
f.close()
for m in re.finditer(regex, s):
doSomething()
有没有办法在不必将整个文件存储在内存中的情况下执行此操作?
注意:逐行读取文件不是一个选项,因为正则表达式可能跨越多行。
更新:如果可能的话,我还希望与stdin
合作。
更新:我正在考虑以某种方式使用自定义文件包装器模拟字符串对象,但我不确定正则表达式函数是否会接受自定义的类似字符串的对象。
答案 0 :(得分:5)
您必须以chunk方式读取文件,使用重叠以允许表达式的最大可能长度,或者使用mmapped文件,这几乎/就像使用流一样好:{{3 }}
更新到您的更新: 考虑到stdin不是一个文件,它只是表现得很像一个文件描述符等等。它是一个posix流。如果你不清楚差异,做一些谷歌搜索。操作系统无法mmap它,因此python不能。
还要考虑你正在做的事情可能是一个不适合使用正则表达式的东西。正则表达式非常适合捕获小东西,比如解析连接字符串,日志条目,csv数据等等。它们不是解析大量数据的好工具。这是设计的。你可能最好写一个自定义解析器。
过去的一些智慧之词: https://docs.python.org/library/mmap.html
答案 1 :(得分:5)
如果可以将正则表达式可以跨越的行数限制为某个合理的数量,则可以使用collections.deque
在文件上创建滚动窗口,并在内存中仅保留该行数。 / p>
from collections import deque
def textwindow(filename, numlines):
with open(filename) as f:
window = deque((f.readline() for i in xrange(numlines)), maxlen=numlines)
nextline = True
while nextline:
text = "".join(window)
yield text
nextline = f.readline()
window.append(nextline)
for text in textwindow("bigfile.txt", 10):
# test to see whether your regex matches and do something
答案 2 :(得分:0)
也许你可以编写一个函数,在文件一次产生一行(读一行)并在其上调用re.finditer,直到它产生一个EOF信号。
答案 3 :(得分:0)
这是另一个解决方案,使用内部文本缓冲区逐步生成找到的匹配项,而不将整个文件加载到内存中。
这个缓冲区就像文件文本中的“滑动窗口”一样,向前移动,同时产生匹配。
由于文件内容是由块加载的,这意味着此解决方案也适用于多行正则表达式。
def find_chunked(fileobj, regex, *, chunk_size=4096):
buffer = ""
while 1:
text = fileobj.read(chunk_size)
buffer += text
matches = list(regex.finditer(buffer))
# End of file, search through remaining final buffer and exit
if not text:
yield from matches
break
# Yield found matches except the last one which is maybe
# incomplete because of the chunk cut (think about '.*')
if len(matches) > 1:
end = matches[-2].end()
buffer = buffer[end:]
yield from matches[:-1]
但是,请注意,如果根本找不到匹配项,最终可能会将整个文件加载到内存中,因此如果您确信文件多次包含正则表达式模式,则最好使用此函数。