我正尝试使用以下正则表达式提取两个短语之间的所有单词:
\b(?:item\W+(?:\w+\W+){0,2}?(?:1|one)\W+(?:\w+\W+){0,3}?business)\b(.*)\b(?:item\W+(?:\w+\W+){0,2}?(?:3|three)\W+(?:\w+\W+){0,3}?legal\W+(?:\w+\W+){0,3}?proceedings)\b
我正在运行此正则表达式的文档为10-K档案。这些文件太长了,无法在此处发布(例如,参见下面的regex101 url),但是基本上它们是这样的:
ITEM 1. BUSINESS
lots of words
ITEM 2. PROPERTIES
lots of words
ITEM 3. LEGAL PROCEEDINGS
我想提取ITEM 1
和ITEM 3
之间的所有单词。请注意,对于每个10-K档案,每个ITEM的字幕可能会略有不同,因此,我允许在每个词之间添加几个词。
我不断收到灾难性的回溯错误,我不知道为什么。例如,请参阅https://regex101.com/r/zgTiyb/1。
我在做什么错了?
答案 0 :(得分:3)
灾难性的回溯几乎有一个主要原因:
您为正则表达式提供了太多职位,无法尝试。这达到了PCRE的回溯限制。一种快速的解决方法是删除正则表达式中唯一的点星,以便将其替换为限制性的量词,即
.{0,200}
但是更好的方法是重新构造正则表达式:
\bitem\b.*?\b(?:1|one)\b(*COMMIT)\W+(?:\w+\W+){0,2}?business\b\h*\R+(?:(?!item\h+(?:3|three)\b)[\s\S])*+item\h+(?:3|three)\b\W+(?:\w+\W+){0,3}?legal\W+(?:\w+\W+){0,3}?proceedings\b
您自己的正则表达式在给定的输入字符串上需要大约45K步才能找到这两个匹配项。相比之下,此经过修改的正则表达式需要大约8000个步骤来完成任务。这是一个巨大的进步。
后者不需要s
标志(并且不应启用)。如果发现可能的匹配但可能无法完成,我使用(*COMMIT)
回溯动词导致了早期失败。
@ Sebastian Proske的解决方案匹配三个子字符串,但我认为第三个匹配不是预期的匹配。如此巨大的第三局比赛是您的正则表达式中断的唯一原因。
请read this answer对这个问题有更好的了解。
答案 1 :(得分:0)
这并不是真正的灾难性回溯,只是大量文本和regex101中相对较低的回溯限制。在这种情况下,使用.*
并不是最佳选择,因为一旦到达文本文件,它将与文本文件的其余部分匹配,然后逐字符回退以匹配其后的部分-这意味着要过程。
似乎您也可以在那个地方坚持使用\w+\W+
,并使用惰性匹配而不是贪婪来获取结果,例如
请注意,pcre引擎将(?:\w+\W+)
优化为(?>\w++\W++)
,从而通过无字块而不是单个字符来工作。