Java正则表达式在堆栈溢出时死亡:需要更好的版本吗

Java正则表达式在堆栈溢出时死亡:需要更好的版本吗,java,regex,Java,Regex,我正在开发一个(的Java端口),但我对一个正则表达式有一个特别的问题。对于此正则表达式终止的文件: private static final String BLOCK_TAGS_1 = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"; private static final String BLOCKS_NESTED_PATTERN = String.f

我正在开发一个(的Java端口),但我对一个正则表达式有一个特别的问题。对于此正则表达式终止的文件:

private static final String BLOCK_TAGS_1 = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";
private static final String BLOCKS_NESTED_PATTERN = String.format("" +
        "(" +                      // save in $1
        "^" +                      // start of line (with MULTILINE)
        "<(%s)" +                  // start tag = $2
        "\\b" +                    // word break
        "(.*\\n)*?" +              // any number of lines, minimally matching
        "</\\2>" +                 // the matching end tag
        "[ \\t]*" +                // trailing spaces/tags
        "(?=\\n+|\\Z)" +           // followed by a newline or end of
        ")", BLOCK_TAGS_1);
这可以通过增加Java的堆栈空间(oss/ss IIRC的默认值为128k/400k)来解决,但是上面的表达式速度很慢

因此,我正在寻找一位能够做得更好的正则表达式大师(或者至少解释这种模式的性能问题)。C版本有点慢,但工作正常。PHP似乎也没有这方面的问题

编辑:这是在Windows 7 64 Ultimate上运行的JDK6u17上。

此部分:

(.*\n)*?
由于嵌套的
*
,并且由于后面必须匹配字符,因此将涉及大量不必要的回溯

我刚刚用perl在一些任意字符串上运行了一个快速基准测试,只需将该部分切换到

(?>.*\n)*?
它不进行非捕获、独立的子分组。这给了您两个好处,它不再浪费时间捕获匹配字符串,更重要的是,它不再在最里面的
*
上回溯,这无论如何都是浪费时间。不可能只有其中的一部分。*会导致有效匹配,因此明确地将其全部或全部设置为零应该会有所帮助


但是,我不知道在这种情况下这是否是一个足够的改进。

虽然改进模式确实有帮助,而且是可取的,但Java的模式匹配器是递归的,通常最好切换到迭代解决方案

当我遇到类似的问题时,我切换到jregex(),这对我很有效

使用改进的解决方案,模式匹配现在可能已经成功,但如果给出的文本大小是原来的10倍,则可能会失败

PS:很抱歉对一个老话题进行死灵巫术,但是这个帖子在google上排名很高,如果我把它放在这里,人们会受益匪浅。

子表达式:
“(.*\\n)*?”
(以及改进的公认答案版本:
”(?>.\n)*?“
),两者都有一个问题:它们无法匹配写在一行上的块元素。换言之,它们无法满足以下条件:

<div>one-liner</div>
一行
如果这不是期望的行为,正确(且更有效)的解决方案是简单地使用:

*?


并打开单行模式。

这是正则表达式的糟糕用法。您是否必须使用正则表达式,或者您是否可以将其重新转换为真正的递归解析器(LR或递归下降)?您是否尝试过使用
*\n
*?\n
?@S.Mark:模式是多行的,但不是全部的,因此我认为所涉及的回溯应该是最小的,但我会尝试。@Jim:我的第一个目标是获得具有广泛单元测试覆盖范围的功能等效的代码版本。正则表达式来自MarkdownSharp(在此之前,可能来自原始的Perl Markdown脚本)。一步一个脚印。@cletus,我明白了,如果模式是多行的应该可以,我认为问题出在
\\2
中,我认为是组
(.*\n)
有一个更复杂的正则表达式,但是忽略捕获的
?>
解决了这个问题!非常感谢。
(?>.*\n)*?
<div>one-liner</div>