【问题标题】:Regex : Catastrophic backtracking when processing large string正则表达式:处理大字符串时的灾难性回溯
【发布时间】:2018-12-17 19:34:51
【问题描述】:

我需要帮助来优化我的正则表达式以处理 URL BBCode 标记。正则表达式用于检查 URL 标记是否具有有效模式并且不包含白名单协议

#(\[url=(?:"|"|\'|)(((((?!https|http|ftp|mailto).)*):(//)?)([^\[\]]*))(?:"|"|\'|)\])(.*)(\[/url\])#siU

正则表达式将忽略:

  • [url="www.example.com"]示例[/url]
  • [url="https://example.com"]示例[/url]
  • [url="http://example.com"]示例[/url]
  • [url="ftp://example.com"]示例[/url]
  • [url="mailto:mail@example.com"]示例[/url]

当匹配时:

  • [url="ymsgr://example.com"]示例[/url]
  • [url="anyprotocol://example.com"]示例[/url]

运行良好且没有问题,直到用户创建长度超过 10000 个字符的字符串数据,这将导致 灾难性回溯

Regex101 Reference Link

【问题讨论】:

    标签: php regex


    【解决方案1】:

    这是一个稍微优化的版本:

    (?:\[url=(?:"|"|\'|)(?:(?:(?:(?:(?!https?|ftp|mailto).)*):(?://)?)(?:(?!"|"|&quote;).)++)(?:"|"|\'|)\])(?:(?!\[/url\]).)++(?:\[/url\])
    

    这里的主要优化是:

    • 将大部分捕获组更改为非捕获组(?:)
    • 更改了 .* 表达式,没有经过调和的贪婪令牌/排除 (?:(?!).)
    • 添加了一些所有格量词++
    • (从协议黑名单切换到白名单也会有很大帮助)

    Demo

    如果您打算经常使用这种模式,可能值得一提的是S|Study PHP regex flag。从描述中猜测,它应该没有用,但可能仍然值得一试。我还没有测试过。

    Sample Code


    关于您更新的示例:最好分两步进行:首先,使用更简单的正则表达式提取 URL 元标记,例如

    \[url=.*\[/url\]

    然后,使用您的原始正则表达式或上面的一个来验证输入格式。

    【讨论】:

    • 感谢您的回复,是的,它在获取 url 链接时比我的正则表达式更快。但是仍然有不明白的地方,我在regex101.com/r/Zlj1jS/3 中更新了我的案例@ 长字符串不在 URL 标记中,而是在整个字符串变量中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 2016-03-24
    • 2016-10-07
    • 2017-09-24
    相关资源
    最近更新 更多