解决RegEx中的灾难性回溯问题

时间:2016-10-15 12:56:38

标签: python regex url ip-address ipv4

我使用RegEx在字符串中查找URL子字符串。 我正在使用的RegEx取自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.   
  3. 制作https小组 - (?:https?|ftp):\/\/)?(?:\S+(?::\S*)?@)可选。
  4.   

最终版本是 -

(?:(?: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似乎非常有前途,并且根据我的要求(与原始版本相比)有显着改进,并且在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

1 个答案:

答案 0 :(得分:2)

灾难性回溯的问题是由模式(?:(?:[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]*)?