注意:这已经在 python2.7 上测试过了。您可能需要在 python 3 中调整一些东西来处理字符串与字节,但希望它不会太痛苦。
内存映射文件可能不适合您的情况(32 位模式会增加连续虚拟内存不足、无法从管道或其他非文件读取等的可能性)。
这是一次读取 128k 块的解决方案,只要您的正则表达式匹配小于该大小的字符串,这将起作用。另请注意,您不受使用单行正则表达式的限制。这个解决方案工作得很快,虽然我怀疑它会比使用 mmap 稍微慢一些。这可能更多地取决于您对匹配项所做的工作,以及您正在搜索的正则表达式的大小/复杂性。
该方法将确保在内存中最多只保留 2 个块。在某些用例中,您可能希望对每个块强制执行至少 1 个匹配作为健全性检查,但此方法将截断以在内存中保留最多 2 个块。它还确保不会产生任何吃到当前块末尾的正则表达式匹配,而是保存最后一个位置,以供当真正的输入用完或者我们在结束之前有另一个正则表达式匹配的块时,在为了更好地匹配“[^\n]+”或“xxx$”等模式。如果您在 xx(?!xyz) 之类的正则表达式末尾有一个前瞻,您仍然可以破坏事情,其中 yz 在下一个块中,但在大多数情况下,您可以使用这些模式来解决问题。
import re
def regex_stream(regex,stream,block_size=128*1024):
stream_read=stream.read
finditer=regex.finditer
block=stream_read(block_size)
if not block:
return
lastpos = 0
for mo in finditer(block):
if mo.end()!=len(block):
yield mo
lastpos = mo.end()
else:
break
while True:
new_buffer = stream_read(block_size)
if not new_buffer:
break
if lastpos:
size_to_append=len(block)-lastpos
if size_to_append > block_size:
block='%s%s'%(block[-block_size:],new_buffer)
else:
block='%s%s'%(block[lastpos:],new_buffer)
else:
size_to_append=len(block)
if size_to_append > block_size:
block='%s%s'%(block[-block_size:],new_buffer)
else:
block='%s%s'%(block,new_buffer)
lastpos = 0
for mo in finditer(block):
if mo.end()!=len(block):
yield mo
lastpos = mo.end()
else:
break
if lastpos:
block=block[lastpos:]
for mo in finditer(block):
yield mo
要测试/探索,你可以运行这个:
# NOTE: you can substitute a real file stream here for t_in but using this as a test
t_in=cStringIO.StringIO('testing this is a 1regexxx\nanother 2regexx\nmore 3regexes')
block_size=len('testing this is a regex')
re_pattern=re.compile(r'\dregex+',re.DOTALL)
for match_obj in regex_stream(re_pattern,t_in,block_size=block_size):
print 'found regex in block of len %s/%s: "%s[[[%s]]]%s"'%(
len(match_obj.string),
block_size,match_obj.string[:match_obj.start()].encode('string_escape'),
match_obj.group(),
match_obj.string[match_obj.end():].encode('string_escape'))
这是输出:
found regex in block of len 46/23: "testing this is a [[[1regexxx]]]\nanother 2regexx\nmor"
found regex in block of len 46/23: "testing this is a 1regexxx\nanother [[[2regexx]]]\nmor"
found regex in block of len 14/23: "\nmore [[[3regex]]]es"
这对于快速解析大型 XML 很有用,在这种情况下,它可以基于作为根的子元素拆分成 mini-DOM,而不必在使用 SAX 解析器时深入处理回调和状态。它还允许您更快地过滤 XML。但我也将它用于许多其他目的。我有点惊讶这样的食谱在网上并不容易获得!
还有一件事:只要传入的流生成 unicode 字符串,在 unicode 中解析就应该工作,如果您使用像 \w 这样的字符类,则需要将 re.U 标志添加到重新编译模式构造。在这种情况下,block_size 实际上是指字符数而不是字节数。