为什么使用捕获组的两个正则表达式的性能差异如此之大?
Why the performance of two regex using capturing groups varies so differently?
我有两个使用相同捕获组的正则表达式,但它们的性能差异很大。
我的编程语言是python3.8.
正则表达式描述
两个正则表达式是
new\s+String\s*(?P<aux1>\(((?:[^()]++|(?&aux1))*)\))
和
([\w_\.]+(?P<aux1>\((?:[^()]++|(?&aux1))*\))*+)\s*\.\s*((?:remove|contains|retain)(?:All)?)\s*(?&aux1)
命名的捕获组 (?P<aux1>\((?:[^()]++|(?&aux1))*\))
匹配成对的 (
和 )
,以及它们之间的内容。例如 ( abc() )
.
第一个正则表达式匹配 new String("This is a string.")
.
这样的字符串
第二个匹配字符串 a.contains(b)
或 a.getCollection().contains( anotherCollection )
如何重现
我想知道针对大输入捕获组的性能,所以我下载了一个开源Github项目elasticsearch,其中包含13724Java个文件。
我编写了一个脚本,运行 逐行针对这些文件的两个正则表达式。但是,我发现第一个正则表达式只使用了 1.6286 秒,而第二个正则表达式使用了 50.6662 秒.
我不知道是什么让第二个正则表达式如此缓慢,因为这两个正则表达式非常相似并且共享相同的捕获组。
问题与嵌套括号模式组无关。
位于模式开头的 [\w_\.]+
部分使一切变得不同。它使用 +
量词进行量化,可以匹配范围广泛的字符、字母、数字、点的任何一次或多次出现,然后还有更多模式可以匹配。当后续模式不匹配时,回溯触发并可以返回到初始模式,并且可能有很多位置可以测试正则表达式。这将导致灾难性的回溯。
解决方案
始终尝试将您的正则表达式模式“锚定”在某个固定模式。在这种情况下,如果您计划在单词边界处进行匹配,请将单词边界模式放在开头并使用 \b\w[\w.]*
而不是 [\w_\.]+
:
(\b\w[\w.]*(?P<aux1>\((?:[^()]++|(?&aux1))*\))*+)\s*\.\s*((?:remove|contains|retain)(?:All)?)\s*(?&aux1)
优化算法将确保只测试单词边界位置,第一个字符必须是单词字符。
我有两个使用相同捕获组的正则表达式,但它们的性能差异很大。
我的编程语言是python3.8.
正则表达式描述
两个正则表达式是
new\s+String\s*(?P<aux1>\(((?:[^()]++|(?&aux1))*)\))
和
([\w_\.]+(?P<aux1>\((?:[^()]++|(?&aux1))*\))*+)\s*\.\s*((?:remove|contains|retain)(?:All)?)\s*(?&aux1)
命名的捕获组 (?P<aux1>\((?:[^()]++|(?&aux1))*\))
匹配成对的 (
和 )
,以及它们之间的内容。例如 ( abc() )
.
第一个正则表达式匹配 new String("This is a string.")
.
第二个匹配字符串 a.contains(b)
或 a.getCollection().contains( anotherCollection )
如何重现
我想知道针对大输入捕获组的性能,所以我下载了一个开源Github项目elasticsearch,其中包含13724Java个文件。
我编写了一个脚本,运行 逐行针对这些文件的两个正则表达式。但是,我发现第一个正则表达式只使用了 1.6286 秒,而第二个正则表达式使用了 50.6662 秒.
我不知道是什么让第二个正则表达式如此缓慢,因为这两个正则表达式非常相似并且共享相同的捕获组。
问题与嵌套括号模式组无关。
位于模式开头的 [\w_\.]+
部分使一切变得不同。它使用 +
量词进行量化,可以匹配范围广泛的字符、字母、数字、点的任何一次或多次出现,然后还有更多模式可以匹配。当后续模式不匹配时,回溯触发并可以返回到初始模式,并且可能有很多位置可以测试正则表达式。这将导致灾难性的回溯。
解决方案
始终尝试将您的正则表达式模式“锚定”在某个固定模式。在这种情况下,如果您计划在单词边界处进行匹配,请将单词边界模式放在开头并使用 \b\w[\w.]*
而不是 [\w_\.]+
:
(\b\w[\w.]*(?P<aux1>\((?:[^()]++|(?&aux1))*\))*+)\s*\.\s*((?:remove|contains|retain)(?:All)?)\s*(?&aux1)
优化算法将确保只测试单词边界位置,第一个字符必须是单词字符。