【问题标题】:Why not create a backreference?为什么不创建反向引用?
【发布时间】:2011-03-14 01:57:42
【问题描述】:

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

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

【问题讨论】:

  • 与编程中的任何事情一样,小型设备的速度永远不值得担心。如果你在兆字节的文本上运行这个正则表达式,那么差异会很大。
  • @Travis,一些执行不力的正则表达式引擎会进行指数回溯,即使是很小的输入也会非常慢。我遇到了将一些 perl 移植到 python 的问题。 Python 已经用它的re 模块解决了很多问题,但是,你在正则表达式极端情况下看到的失败模式可能是 O(2**n) 最坏的情况。
  • @Mike 我听说过可怕的回溯实现,但是你如何达到 O(2^n)?其中 n = ?

标签: regex backreference


【解决方案1】:

我认为您混淆了像 \1 这样的反向引用和捕获组 (...)

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

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

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

编辑:

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

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

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

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

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

【讨论】:

  • 不错。 +1 为他甚至无法正确提出的问题提供正确的解决方案。
  • 这不是一个好的答案,我没有将反向引用的创建与捕获组混淆。这个答案根本没有回答这个问题。我问为什么强制正则表达式阻止创建反向引用(使用捕获组)。没有混乱。至于答案,第二段有很好的回应,但您没有提供任何解释或示例。使语言不规则是什么意思?我不关心捕获组与反向引用的比较,我只是在谈论跳过反向引用创建
  • @tandu,我不知道什么是“强制正则表达式阻止创建反向引用”。正则表达式包含捕获组。通常只有捕获组 1 到 9 可以在替换字符串中引用为 $1...$9,因此这是不让所有括号组都成为捕获组的原因之一。反向引用与捕获组不同。反向引用是出现在正则表达式(而不是替换字符串)中的序列,它引用回捕获组,perl 5 对它们使用\1...\9 语法。
  • 你是什么意思“通常只有组 1-9 可以被引用为 $1$9 在替换中”? Perl 当然允许你使用尽可能多的编号组,所以你可以说,$388,如果你如此无情地倾向于。其他语言是否会对这类事情施加任意限制?
  • @tchrist,一些例子。来自grymoire.com/Unix/Regular.html#uh-10“您可以用“\”后跟一个数字来调用记住的模式。因此,要搜索两个相同的字母,请使用“([a-z])\1”。您可以有 9 种不同的记住模式。 Python 最多允许 99 个:docs.python.org/library/re.html。我相信Java是无限的。 JavaScript 允许超过 9 个,但我没有测试过限制。
【解决方案2】:

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

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

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

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

【讨论】:

  • perl5 语法的问题是做你想做的事情很混乱,即使用大量(?:⋯) 进行简单的未命名分组,使用(?<ɴᴀᴍᴇ>⋯) 进行命名捕获和@ 987654324@ 用于命名的反向引用。尽管比 (⋯)\1$1 要好得多,但它们都更加冗长/嘈杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-22
  • 1970-01-01
  • 2020-08-04
  • 2021-08-12
相关资源
最近更新 更多