我有多个文件,每个文件都在搜索一系列文字。
我的正则表达式基本上搜索一个序列,其中word1后跟word2,后跟word 3等。 所以表达式如下:
strings = re.findall('word1.*?word2.*?word3', f.read(), re.DOTALL)
对于低于20kb的文件,表达式执行得非常好。但是,文件的执行时间呈指数增长> 20 kb,该过程完全挂起接近100kb的文件。 看起来(在阅读了之前的线程之后)问题与使用。*和re.DOTALL一起使用 - 导致“灾难性的回溯”。建议的解决方案是逐行提供输入文件,而不是将整个文件读入单个内存缓冲区。
但是,我的输入文件中填充了随机空格和“\ n”换行符。我的单词序列也很长并且发生在多行上。因此,我需要将整个文件与re.DOTALL一起输入到regex表达式中 - 否则逐行搜索将永远不会找到我的序列。
周围有什么办法吗?
答案 0 :(得分:2)
如果你真的在搜索三个单词的出现,根本没有正则表达式模式,根本就没有必要使用正则表达式 - 正如@Bart所建议的那样,我写了这个答案:)。像这样的东西可能会起作用(未经测试,可能更漂亮):
with open('...') as f:
contents = f.read()
words = ['word1', 'word2', 'word3']
matches = []
start_idx = 0
try:
while True:
cand = []
for word in words:
word_idx = contents.index(word, start_idx)
cand.append(word_idx)
start_idx = word_idx + len(word)
matches.append(cand)
except ValueError: # from index() failing
pass
这将指数放在matches
;如果你想要一个等效的结果,你可以做,比方说,
found = [contents[match[0]:match[-1]+len(words[-1]] for match in matches]
您也可以通过使用文件上的等效函数替换对index
的调用来预先读取整个文件,从而使这种方法工作。我不认为stdlib包含这样的功能;你可能不得不在文件对象上手动使用readline()
和tell()
或类似的方法。
答案 1 :(得分:1)
发生这种情况的原因是因为python的正则表达式引擎使用了回溯。在每.*
,如果找不到下面的单词,引擎必须一直到字符串的末尾(100kb),然后回溯。现在考虑如果在最后一场比赛后有很多“几乎匹配”会发生什么。引擎从匹配开始到字符串结尾不断跳来跳去。
您可以使用基于NFA而非回溯的正则表达式引擎来修复它。请注意,这限制了您可以使用的正则表达式的类型(没有回溯或任意零宽度断言),但它适用于您的用例。
您可以找到这样的引擎here。您可以在www.debuggex.com处可视化nfa引擎的工作原理。
答案 2 :(得分:0)
您可以使用循环一次搜索一个单词。我在这里使用str.find()
,因为它对于简单的子字符串搜索更快,但您也可以调整此代码以使用re.search()
代替。
def findstrings(text, words):
end = 0
while True:
start = None
for word in words:
pos = text.find(word, end) #starts from position end
if pos < 0:
return
if start is None:
start = pos
end = pos + len(word)
yield text[start:end]
#usage in place of re.findall('word1.*?word2.*?word3', f.read(), re.DOTALL)
list(findstrings(f.read(), ['word1', 'word2', 'word3']))