Python Regex re.findall()挂起-如果不能逐行阅读怎么办

Python Regex re.findall()挂起-如果不能逐行阅读怎么办,python,regex,freeze,Python,Regex,Freeze,我有多个文件,每个文件我都在搜索一系列单词 我的正则表达式基本上搜索一个序列,其中word1后面跟着word2,后面跟着Word3等等。。 所以这个表达式看起来像: strings = re.findall('word1.*?word2.*?word3', f.read(), re.DOTALL) 对于20kb以下的文件,表达式执行得非常好。但是,对于大于20KB的文件,执行时间会成倍增加,而对于接近100kb的文件,执行过程会完全挂起。 在阅读了前面的线程之后,问题似乎与将.*与re.DOT

我有多个文件,每个文件我都在搜索一系列单词

我的正则表达式基本上搜索一个序列,其中word1后面跟着word2,后面跟着Word3等等。。 所以这个表达式看起来像:

strings = re.findall('word1.*?word2.*?word3', f.read(), re.DOTALL)
对于20kb以下的文件,表达式执行得非常好。但是,对于大于20KB的文件,执行时间会成倍增加,而对于接近100kb的文件,执行过程会完全挂起。 在阅读了前面的线程之后,问题似乎与将.*与re.DOTALL结合使用有关-导致“灾难性回溯”。建议的解决方案是逐行提供输入文件,而不是将整个文件读入单个内存缓冲区

但是,我的输入文件中填充了随机空格和“\n”换行符。我的单词序列也很长,出现在多行上。因此,我需要将整个文件与re.DOTALL一起输入regex表达式,否则逐行搜索将永远找不到我的序列


有什么办法可以解决吗?

发生这种情况的原因是python的正则表达式引擎使用回溯。每次
*
,如果未找到以下单词,引擎必须一直运行到字符串末尾(100kb),然后返回。现在考虑一下如果在最后一场比赛后有很多“几乎匹配”会发生什么。引擎从比赛开始到比赛结束不断地来回跳跃

您可以通过使用基于NFA的正则表达式引擎而不是回溯来修复它。请注意,这限制了您可以使用的正则表达式的种类(没有回溯或任意的零宽度断言),但这对于您的用例来说是很好的


你可以找到这样的引擎。你可以想象nfa引擎是如何工作的。

如果你真的在搜索三个单词的出现,而这些单词中根本没有正则表达式模式,那么根本就不需要使用正则表达式——正如我在写这个答案时@Bart所建议的:)。这样做可能会奏效(未经测试,可能会更漂亮):

这将索引置于
匹配项中
;如果你想得到与findall相同的结果,你可以

found = [contents[match[0]:match[-1]+len(words[-1]] for match in matches]

您还可以将对
index
的调用替换为对文件的等效函数,从而使这种方法在不事先读取整个文件的情况下工作。我不认为stdlib包含这样的功能;您可能必须在文件对象上手动使用
readline()
tell()
或类似的方法。

您可以使用循环一次搜索一个单词。我在这里使用
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']))
def findstrings(文本、文字):
结束=0
尽管如此:
开始=无
用文字表示:
pos=文本。查找(单词,结束)#从位置结束开始
如果位置<0:
返回
如果“开始”为“无”:
开始=位置
结束=位置+长度(字)
产生文本[开始:结束]
#替代re.findall('word1.*.word2.*.word3',f.read(),re.DOTALL)的用法
列表(查找字符串(f.read(),['word1','word2','word3']))

尝试在f.readlines()中执行此操作,并逐行对其进行正则化line@karthikr如果整个东西都是随机的空格和换行符,那怎么可能呢?我以为f.read()是一个文件read问题是如果我逐行正则化它,搜索将永远找不到我的序列-我的序列不会出现在一行中…但只出现在多行上,因为存在关于使用纯字符串操作的随机“\n”字符?获取
word1
word2
word3
的索引,然后检查这些索引?在最坏的情况下,整个文件没有匹配项,所以是的。我使用了短语“when't not find the following word”,但我会将其更改为更清晰。只有当它到达单词末尾时才会失败,然后回到它可能采取的任何其他路径,最终到达比赛的开始。然后,它将继续从那里,直到它找到另一个“比赛的开始”。仔细想想如果我的文件结尾看起来像
Word1Word1Word1Word1
,会发生什么情况。不,只有在尝试从每个可能的起始位置进行匹配后才会失败。是的,再仔细想想,在阅读了您编辑的答案(许多“几乎匹配”部分)后,我认为您是对的。我已经提到了,但我要再说一遍:整洁的工具!是的,在这种情况下,这是一个比正则表达式好得多的选项。虽然这可能是正确的,但它非常冗长,而且不可能一眼就知道它做了什么。它可以作为练习使用,但实际上不应该在这个用例的生产中使用。@SergiuToarca,Python读起来很像伪代码。就个人而言,我会选择一个(有文档记录的)版本,而不是生产中的正则表达式解决方案。特别是如果核心语言中没有提供regex引擎,我希望使用内置的
查找
。当然,所有的IMHO。@SergiuToarca基于NFA的正则表达式都很好,我想知道你的答案,但有时你不能添加依赖项,或者不值得。我还认为对于一个非平凡的任务,15行相当简单的代码加上一些注释并不算“极其冗长”。:)同意,尽管我仍然认为它过于冗长:p
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']))