使用Java / Javascript的正则表达式/正则表达式:性能下降或无限循环

时间:2018-01-17 08:59:43

标签: regex performance

我想在这里提交一个我想要了解的非常具体的性能问题。

目标

我正在尝试使用正则表达式验证自定义合成器。通常,我没有遇到性能问题,所以我喜欢使用它。

案例

正则表达式:

^(\{[^\][{}(),]+\}\s*(\[\s*(\[([^\][{}(),]+\s*(\(\s*([^\][{}(),]+\,?\s*)+\))?\,?\s*)+\]\s*){1,2}\]\s*)*)+$

有效的合成器:

{Section}[[actor1, actor2(syno1, syno2)][expr1,expr2]][[actor3,actor4(syno3, syno4)][expr3,expr4]]

你可以在这里找到正则表达式和测试文本: https://regexr.com/3jama

我希望足够了,我不知道如何用正则表达式解释我想要匹配的内容; - )。

问题

在有效文本上应用正则表达式并不需要花费太多,它几乎是即时的。 但是当涉及到特定的无效文本案例时,regexr应用程序会挂起。它并不是特定于regexr应用程序,因为我还使用自己的java代码或javascript代码进行了戏剧性的表演。

因此,我的需求是验证用户正在键入文本。我甚至可以想象在点击时验证文本,但是如果用户提交的文本结构如下所示,或者产生相同性能下降的其他文本,我无法承担应用程序将挂起。

重现问题

只需从测试文本中删除尾随的“]”字符

即可

因此,提高性能下降的无效文本变为:

{Section}[[actor1, actor2(syno1, syno2)][expr1,expr2]][[actor3,actor4(syno3, syno4)][expr3,expr4

另一个无效的测试可能是,并且没有性能下降:

{Section}[[actor1, actor2(syno1, syno2)][expr1,expr2]][[actor3,actor4(syno3, syno4)][expr3,expr4]]]

请求

如果一个正则表达的大师能够解释我的错误,或者为什么我的用例不适合正则表达式,我会很高兴。

1 个答案:

答案 0 :(得分:3)

这个答案适用于您评论中的精简正则表达式:

^(\{[^\][{}(),]+\}(\[(\[([^\][{}(),]+(\(([^\][{}(),]+\,?)+\))?\,?)+\]){1,2}\])*)+$

原始模式的问题类似。

你正面临着灾难性的回溯。每当正则表达式引擎无法完成匹配时,它就会回溯到字符串中,试图找到将模式与某些子字符串匹配的其他方法。如果你有很多模糊的模式,特别是如果它们出现在重复内部,测试所有可能的变化需要花费很多时间。有关更好的解释,请参阅link

您使用的子模式之一是以下(为了更好的可视化而采用多行模式):

([^\][{}(),]+
  (\(
    ([^\][{}(),]+\,?)+
  \))?
\,?)+

应该匹配像actor4(syno3, syno4)这样的字符串。稍微冷凝这种模式,你得到([^\][{}(),]+,?)+。如果从中移除,?,则会得到([^\][{}(),]+)+,这是营养回溯的开门,因为字符串可以通过这种模式以多种不同方式匹配。

我得到你试图用这个模式做的事情 - 匹配一个标识符 - 以及用逗号分隔的其他其他标识符。然而,正确的方法是:([^\][{}(),]+(?:,[^\][{}(),]+)*)。现在没有一种模糊的方式可以回溯到这种模式。

对于上面显示的整个模式执行此操作(是的,还有另一个可选的逗号,必须推出)并将其插回到完整模式中,我将:

^(\{[^\][{}(),]+\}(\[(\[([^\][{}(),]+(\(([^\][{}(),]+(?:,[^\][{}(),]+)*)\))?(?:\,[^\][{}(),]+(\(([^\][{}(),]+(?:,[^\][{}(),]+))*\))?)*)\]){1,2}\])*)+$

这不会灾难性地回溯anymore

您可能希望自己帮忙并将其拆分为子模式,您可以使用实际源中的字符串连接在一起,或者如果使用PCRE模式则使用定义。

请注意,一些正则表达式引擎允许使用原子组和占有量词,进一步帮助避免不必要的回溯。由于您在标题中使用了不同的语言,因此您必须自己检查,哪一种可用于您选择的语言。