修正正则表达式中的灾难性回溯

时间:2017-08-02 14:04:34

标签: regex backtracking

问题

我正在使用以下正则表达式来检查有效的文件路径:

^(?:[a-zA-Z]\:\\|\\\\)([^\\\/\:\*\?\<\>\"\|]+(\\){0,1})+$

使用测试字符串V:\Sample Names\Libraries\DeveloperLib\DeveloperComDlgs\res被视为有效。我甚至可以在字符串的开头添加无效字符而不会出现问题。但是,当我在字符串的末尾添加无效字符时,网页会从灾难性的回溯中冻结。

在这个正则表达式字符串中导致这种情况的原因是什么?

打破正则表达式

完整字符串: ^(?:[a-zA-Z]\:\\|\\\\)([^\\\/\:\*\?\<\>\"\|]+(\\){0,1})+$

第一组: (?:[a-zA-Z]\:\\|\\\\)

  • 检查两者
    • 大写或小写字母后跟冒号和反斜杠
    • 双反斜杠

第二组: ([^\\\/\:\*\?\<\>\"\|]+(\\){0,1})

  • 第一部分: [^\\\/\:\*\?\<\>\"\|]+
    • 确保没有非法字符(\ /:*?&lt;&gt;“|)
  • 第二部分: (\\){0,1}
    • 根据需要多次检查各部分之间的反斜杠

我认为可能导致问题的{0, 1}因为这允许回溯但我不确定。有什么想法吗?

1 个答案:

答案 0 :(得分:2)

您当前的正则表达式可以写成^(?:[a-zA-Z]:\\|\\\\)([^\\\/\:*?<>"|]+\\?)+$:在? {0,1}之后注意\\量词(它等于+限制量词)量化组。

一旦这样的模式如(a+b?)+存在于模式中,就很有可能发生灾难性的回溯。比赛时,一切都很好,比如c:\12\34\aaaaaaaaaaaaaaaaaaa is matched fine,但是一旦不允许的字符显示导致不匹配,(尝试在最后添加*c:\12\34\aaaaaaaaaaaaaaaaaaa*) ,issue will appear

要解决此问题,可以匹配相同文本的量化子模式不能立即相继跟随。并且使用可选的组,其中每个子模式都是强制性的。

在大多数情况下,您需要使用展开的a+(ba+)*替换这些模式部分(一次或多次出现a后跟0个或多个b序列(不再是可选的)本身)然后发生一次或多次a(因此,在一个a和下一个a之间必须有一个b。如果你需要匹配一个\最后的可选^(a+b?)+$(因为b实际上可能与字符串末尾的b?匹配),您需要在结尾处添加a+(ba+)*b?:{{1} }。

所以,将其转换为您当前的场景:

^(?:[a-zA-Z]:\\|\\\\)[^\\\/\:*?<>"|]+(?:\\[^\\\/\:*?<>"|]+)*$

或者如果最后允许\

^(?:[a-zA-Z]:\\|\\\\)[^\\\/\:*?<>"|]+(?:\\[^\\\/\:*?<>"|]+)*\\?$
                     |      a+       (   b       a+       )* b?

查看不匹配时的fails gracefullymatches as expected

正如@anubhava建议的那样,你可以通过使用所有权量词(或原子组代替,因为,例如.NET正则表达式引擎不支持所有权)来进一步增强性能禁止任何回溯进入分组模式。一旦匹配,这些模式就不会被重新尝试,因此,失败可能会更快:

^(?:[a-zA-Z]:\\|\\\\)[^\\\/\:*?<>"|]+(?:\\[^\\\/\:*?<>"|]+)*+\\?$
                                                            ^

或原子组示例:

^(?:[a-zA-Z]:\\|\\\\)(?>[^\\\/\:*?<>"|]+(?:\\[^\\\/\:*?<>"|]+)*)\\?$
                     ^^^                                       ^                          

请注意:不是特殊的正则表达式元字符,不应对其进行转义。在角色类中,只有-^\]通常需要转义,其他所有其他都不是特别的。

The Explosive Quantifier Trap查看有关灾难性回溯的详情。