尝试在java中使用正则表达式时出现堆栈溢出

尝试在java中使用正则表达式时出现堆栈溢出,java,html,regex,recursion,stack-overflow,Java,Html,Regex,Recursion,Stack Overflow,我读过一些关于如何优化正则表达式的文章,但是没有一个答案(更少的组,使用{X,Y}而不是*)似乎能阻止我的正则表达式出现堆栈溢出错误 我正在尝试通过文件进行动态搜索。假设我正在一个相当大(2-4MB)的文件中搜索“我打赌你找不到我”。我的正则表达式生成器将生成正则表达式: i(?:.|\s)*?bet(?:.|\s)*?you(?:.|\s)*?cannot(?:.|\s)*?find(?:.|\s)*?me 这个正则表达式的思想是,无论单词之间有什么字符或空格,它都能找到准确的短语。但是,当

我读过一些关于如何优化正则表达式的文章,但是没有一个答案(更少的组,使用{X,Y}而不是*)似乎能阻止我的正则表达式出现堆栈溢出错误

我正在尝试通过文件进行动态搜索。假设我正在一个相当大(2-4MB)的文件中搜索“我打赌你找不到我”。我的正则表达式生成器将生成正则表达式:

i(?:.|\s)*?bet(?:.|\s)*?you(?:.|\s)*?cannot(?:.|\s)*?find(?:.|\s)*?me
这个正则表达式的思想是,无论单词之间有什么字符或空格,它都能找到准确的短语。但是,当我尝试使用:

Pattern p = Pattern.compile(generatedRegex, Pattern.MULTILINE);
Matcher m = p.matcher(fileContentsAsString);
while (m.find()) {
System.out.println(m.group())
}
我得到一个堆栈溢出错误。我知道正则表达式使用递归,但它似乎并没有正则表达式那么糟糕。有什么方法可以优化这个正则表达式吗?谢谢

答复:

Pattern p = Pattern.compile("i(?:.*)bet(?:.*)you(?:.*)cannot(?:.*)find(?:.*?)me", Pattern.DOTALL);

是我最终使用的模式/正则表达式。看起来速度很快,不再出现堆栈溢出异常

我认为由于您不情愿的限定符
(*?)
,您会得到很多回溯。防止回溯的一种方法是使用原子分组
(?>X)
,和/或所有格限定符
(*+)

根据评论,您也更喜欢只捕获最接近“下注”的“i”,以缩短整个比赛的长度。既然你想得到最接近其余单词的“i”,那么在我为单词2添加了负向前瞻的地方,你也可以在单词1旁边添加负向前瞻。换句话说,
(?!bet)
将变成
(?!i)(?!bet)
(?!i | bet)
。我已经编辑了下面的代码以包含此要求

String fileContentsAsString = "ii ... bet ... you, ibetyouyou";
String regex = "i(?>(?!i|bet).)*+bet(?>(?!you).)*+you";
Pattern p = Pattern.compile(regex, Pattern.DOTALL);
Matcher m = p.matcher(fileContentsAsString);
while (m.find()) {
    System.out.println(m.group());
}
输出:

我。。。。打赌你

伊贝图

说明

“不情愿的量词的工作方式是,每次它尝试匹配时,它首先尝试让正则表达式的下一部分匹配。因此,它在每次迭代开始时有效地进行前瞻,这可能会非常昂贵,特别是当量化部分每次迭代只匹配一个字符时,如。*?”


(?:.|\s)
的目的是什么?是因为
与行终止符不匹配,而您希望它匹配吗?如果是这样,请使用mode,即
(?s)
,如下所示:
(?s)i.*?bet.*?you.*?cannot.*?find.*?me
您是否尝试过使用贪婪的
*
而不是勉强使用的限定符
*?
,因为您的正则表达式没有
^
$
,所以该模式毫无意义。使用dotall之后,删除不必要的不情愿的限定符,去掉不必要的多行选项,它确实可以很快成功运行。没有更多的堆栈溢出!谢谢你的评论@BlahMclean在每个单词周围添加捕获组,然后打印每个单词的开始索引,而不是整个匹配的文本。在我看来,你得到的似乎都是想要的组,如果我错了,请更正。@S.Klumpers not true。@op中的
(?:.|\s)
都是非捕获组。他只捕获了
group()
,这是完全匹配的。这是真的,那些是非捕获组,因为我不在乎单词之间的内容@PatrickParker我转而使用dotall,去掉了不必要的勉强限定符,从而消除了堆栈溢出异常。在这一点上,很难说您建议的其他更改将在多大程度上提高性能,但我非常感谢您在原子分组方面对我进行的教育!使用上述解决方案后,我的正则表达式现在捕捉到了剩余模式之前的第一个“i”(这可能是我文件的一大部分)。我怎样才能告诉正则表达式得到与其余单词最接近的“I”?@BlahMclean在这种情况下,在我为单词2添加负前瞻的地方,你也会在单词1旁边添加负前瞻。换句话说,
(?!bet)
将变成
(?!i|bet)