我知道将?:
正则表达式的括号放在开头会阻止它创建反向引用,这应该会更快。我的问题是,为什么要这样做?速度的提高是否足以值得考虑?在什么情况下,它会变得如此重要,以至于您每次不打算使用它时都需要小心地跳过反向引用。另一个缺点是它使正则表达式更难阅读、编辑和更新(如果您最终想稍后使用反向引用)。
总而言之,为什么不创建反向引用呢?
我知道将?:
正则表达式的括号放在开头会阻止它创建反向引用,这应该会更快。我的问题是,为什么要这样做?速度的提高是否足以值得考虑?在什么情况下,它会变得如此重要,以至于您每次不打算使用它时都需要小心地跳过反向引用。另一个缺点是它使正则表达式更难阅读、编辑和更新(如果您最终想稍后使用反向引用)。
总而言之,为什么不创建反向引用呢?
我认为您正在混淆反向引用\1
和捕获组(...)
。
反向引用通过使语言不规则来防止各种优化。
捕获组使正则表达式引擎做更多的工作来记住组的开始和结束位置,但不如反向引用那么糟糕。
http://www.regular-expressions.info/brackets.html详细解释了捕获组和对它们的反向引用。
编辑:
在反向引用使正则表达式不规则时,请考虑以下匹配 lua 注释的正则表达式:
/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/
所以--[[...]]
是评论,--[=[...]=]
是评论,--[==[...]==]
是评论。您可以通过在方括号之间添加额外的等号来嵌套注释。
这无法通过严格的常规语言匹配,因此简单的有限状态机无法在 O(n) 时间内处理它——你需要一个计数器。
Perl 5 正则表达式可以使用反向引用来处理这个问题。但是一旦你需要非常规模式匹配,你的正则表达式库就必须放弃简单的状态机方法,而使用更复杂、效率更低的代码。
你是对的,性能并不是避免捕获组的唯一原因——事实上,它甚至不是最重要的原因。
另一个缺点是它使正则表达式更难阅读、编辑和更新(如果您最终想稍后使用反向引用)。
我反过来看:如果您习惯性地使用非捕获组,那么当您选择捕获某些东西时,更容易跟踪组号。同样,如果您使用命名组(假设您的正则表达式支持它们),则应始终使用命名组,并始终按名称而不是编号来引用它们(在反向引用或替换字符串中)。始终遵循这些规则将至少部分抵消非捕获组的可读性损失。
是的,这是一个 PITA 必须以这种方式混淆你的正则表达式,编写/维护正则表达式实现的人都知道这一点。在 .NET 中,您可以设置ExplicitCapture
选项,从而将所有“裸”括号视为非捕获组,并且仅捕获命名组。在 Perl 6 中,括号(带或不带名称)总是捕获,方括号用于非捕获组。其他口味最终可能会效仿,但与此同时,我们只需要依靠良好的习惯即可。