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



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

目标

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

正则表达式:

^({[^][{}(),]+}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

我希望这足够了,我不知道如何解释我想要匹配的东西而不是正则表达式;-)。

问题

在有效文本上应用正则表达式的成本并不高,几乎是即时的。 但是当涉及到特定的无效文本大小写时,正则表达式应用程序会挂起。它不是特定于正则表达式应用程序,因为我也遇到了我自己的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,2}])*)+$

这些问题与您的原始模式类似。

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

您使用的子模式之一是以下内容(多行线以获得更好的可视化):

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

这应该匹配像actor4(syno3, syno4)这样的字符串。再压缩一下这个模式,你就可以([^][{}(),]+,?)+了。如果你从中移除,?,你会得到([^][{}(),]+)+这是通向折返回退的打开门,因为字符串可以以许多不同的方式与这种模式匹配。

我明白你试图用这种模式做什么 - 匹配一个标识符 - 也许还有其他用逗号分隔的标识符。但是,执行此操作的正确方法是:([^][{}(),]+(?:,[^][{}(),]+)*).现在没有一种模棱两可的方法可以回溯到这种模式。

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

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

这不会再灾难性地回溯了。

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

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

最新更新