5

我知道将?:正则表达式的括号放在开头会阻止它创建反向引用,这应该会更快。我的问题是,为什么要这样做?速度的提高是否足以值得考虑?在什么情况下,它会变得如此重要,以至于您每次不打算使用它时都需要小心地跳过反向引用。另一个缺点是它使正则表达式更难阅读、编辑和更新(如果您最终想稍后使用反向引用)。

总而言之,为什么不创建反向引用呢?

4

2 回答 2

13

我认为您正在混淆反向引用\1和捕获组(...)

反向引用通过使语言不规则来防止各种优化。

捕获组使正则表达式引擎做更多的工作来记住组的开始和结束位置,但不如反向引用那么糟糕。

http://www.regular-expressions.info/brackets.html详细解释了捕获组和对它们的反向引用。

编辑:

在反向引用使正则表达式不规则时,请考虑以下匹配 lua 注释的正则表达式:

/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/

所以--[[...]]是评论,--[=[...]=]是评论,--[==[...]==]是评论。您可以通过在方括号之间添加额外的等号来嵌套注释。

这无法通过严格的常规语言匹配,因此简单的有限状态机无法在 O(n) 时间内处理它——你需要一个计数器。

Perl 5 正则表达式可以使用反向引用来处理这个问题。但是一旦你需要非常规模式匹配,你的正则表达式库就必须放弃简单的状态机方法,而使用更复杂、效率更低的代码。

于 2011-03-14T02:01:00.943 回答
6

你是对的,性能并不是避免捕获组的唯一原因——事实上,它甚至不是最重要的原因。

另一个缺点是它使正则表达式更难阅读、编辑和更新(如果您最终想稍后使用反向引用)。

我反过来看:如果您习惯性地使用非捕获组,那么当您选择捕获某些东西时,更容易跟踪组。同样,如果您使用命名组(假设您的正则表达式支持它们),则应始终使用命名组,并始终按名称而不是编号来引用它们(在反向引用或替换字符串中)。始终遵循这些规则将至少部分抵消非捕获组的可读性损失。

是的,这是一个 PITA 必须以这种方式混淆你的正则表达式,编写/维护正则表达式实现的人都知道这一点。在 .NET 中,您可以设置ExplicitCapture选项,从而将所有“裸”括号视为非捕获组,并且仅捕获命名组。在 Perl 6 中,括号(带或不带名称)总是捕获,方括号用于非捕获组。其他口味最终可能会效仿,但与此同时,我们只需要依靠良好的习惯即可。

于 2011-03-14T10:20:55.380 回答