【问题标题】:Resolving Catastrophic Backtracking issue in RegEx解决 RegEx 中的灾难性回溯问题
【发布时间】:2017-02-24 20:05:56
【问题描述】:

我正在使用 RegEx 在字符串中查找 URL 子字符串。 我正在使用的正则表达式取自 tohster 的回答 - What's the cleanest way to extract URLs from a string using Python?

RE 是 -

r'^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$'

我已经对其进行了一些更改-

  1. 在 IPv4 检测部分,我更改了要查找的 IP 范围的顺序。 > 准确地说,在 2 个实例中将 [1-9]\d?|1\d\d|2[01]\d|22[0-3] 更改为 25[0-5]|2[0-4][0-9]|1[0-> 9]{2}|[1-9][0-9]|[0-9]
  2. 将 https 组 - (?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@) 设为可选。

最终版本是-

(?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?

我正在使用的最终 RE 似乎很有前途,并且根据我的要求(与原始 RE 相比)有了显着改进,并且可以在 Python 和 Java Script 中工作,除了由于我所做的更改已经导致以下示例给出"catastrophic backtracking" 错误-

asasasasasac31.23.53.122asasassasd

12312312312321.32.34.2312312312321

12.3423423432.234123123.123

31.134232131.231.34

可以在-https://regex101.com/r/i6jDei/1进行测试

我的论点是第一个示例 - asasasasasac31.23.53.122asasassasd 应该有一些巧妙的方式来传递,因为 IP 被非数字字符包围。

另外,有没有办法将上述前两个示例作为有效的 IPv4 地址传递?

为了解决歧义,我会选择尽可能大的地址,即,

31.23.53.122

21.32.34.231

【问题讨论】:

    标签: python regex url ip-address ipv4


    【解决方案1】:

    灾难性回溯问题是由模式(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))引起的,其中(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)会跳过很多组合,如果整体模式无法匹配。如您所见,字符类基本相同,例如对于asasasasasac31,它可以匹配如下:

    (asasasasasac31)
    (a)(sasasasasac31)
    (a)(s)(asasasasac31)
    (as)(asasasasac31)
    

    这并不是它实际采用的方式,只是为了显示存在多少组合。

    这里的错误似乎是- 是可选的,我认为没有理由。如果我们删除 -,我们让它适用于您的样本(并减少已经工作的样本的步骤数)。

    查看更新后的regex101-demo,我还在其中添加了导致灾难性回溯的样本。

    那么最终的模式是:

    (?:(?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)?(?:((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])|(?:(?:[a-z\u00a1-\uffff0-9]+-)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?
    

    【讨论】:

    • 很好的答案
    • 感谢您的发现!我有很多带有“-”字符的 URL。它们是部分输出的,而不是完整的 URL。见regex101.com/r/REu26j/1。这是你做出改变的结果吗?我该怎么做才能获得完整的网址
    • @user1412066 如果我将您的第一个示例输入到您的旧模式中,则匹配是相同的。对于第二个,它显示了灾难性的回溯。
    • 看来双 - 是问题所在,您可能想用 -+ 替换模式中出现的两次 -
    • @user1412066 这显然取决于输入和实际想要达到的目标,但我想说至少 99.9% 的灾难性回溯都可以解决。这可能包括对所用模式的彻底改造。
    猜你喜欢
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 2016-03-24
    • 1970-01-01
    • 2020-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多