【问题标题】:Which one of these cases involves backtracking?其中哪一种情况涉及回溯?
【发布时间】:2019-02-06 18:37:50
【问题描述】:

我正在尝试从多个来源研究正则表达式,但遇到了关于回溯的困惑,因为一个定义回溯,它意味着正则表达式引擎无法匹配模式时的状态,因此它回溯到位置 匹配第一个原子的位置,例如,要匹配He captured a catfish for his cat 中的cat,引擎将如下所示:

  • 它将搜索c,直到匹配到c in captured
  • 那么a也一样
  • 但无法将tp 匹配
  • 引擎在captured 中重置到c 之后的位置,因为它知道在此之前不会发生匹配。

因此,在所有没有量词的情况下,引擎将重置整个模式以尝试从不同位置再次匹配它。

另一个将回溯定义为使用像.* 这样的量词的状态,因此正则表达式引擎将匹配完整的文本,从而使其失败,因此它会一个接一个地回溯,直到匹配发生。我认为这些不一样。

here所述:

正则表达式匹配的一个基本特征涉及称为回溯的概念。它被所有正则表达式量词使用(在需要时),即 *、*?、+、+?、{n,m} 和 {n,m}?。

要匹配一个正则表达式,整个正则表达式必须匹配,而不仅仅是它的一部分。因此,如果包含量词的模式的开头成功导致模式中的后面部分失败,则匹配引擎会备份并重新计算开头部分 - 这就是它被称为回溯的原因。

这意味着像 ([A-Z][A-Z0-9]*)\b[^>]*>.*<\/\1> 这样匹配 Testing <B><I>bold italic</I></B> text. 的模式将像这样工作:

  • 它将首先匹配<B>
  • 那么模式需要匹配.*,它将匹配到字符串的末尾。
  • 然后它会尝试匹配<,但它已经到达末尾,所以它会一个一个地回溯直到匹配。

与第一个 cat 示例相反,它将引擎完全重置到第一个原子并从匹配第一个原子的位置重新开始。

但在另一种情况下,如果我在.* 之后添加?,正则表达式会跳过这个原子.*? 尝试匹配剩余的字符,但如果不匹配,它会回退到. 匹配直到有一个<,然后在它之后开始匹配原子。

我认为这里有多种定义,谁能解释一下这些案例中的哪一个是回溯。

【问题讨论】:

  • 其实是的,它们是一样的。考虑ca+?t(它使用一个量词)——它的回溯与您的样本输入上的cat 完全相同
  • @WiktorStribiżew 我认为你是对的,所以我对这个问题进行了一些编辑,我需要重新考虑并添加关于每个案例的更多细节,并以你的评论作为答案。
  • 网上看了一些,看来回溯的重点是"正则表达式引擎回到之前保存的状态继续搜索匹配”.
  • 当存在非固定长度的量词和交替构造时,可能会发生这种情况。只是 matching 的方式与贪婪(尽可能地抓取,然后从抓取的文本末尾逐个字符地产生字符以适应下一个模式)和非贪婪(非-首先跳过贪婪模式,测试所有后续模式,然后“扩展”非贪婪模式,再抓取一个字符,然后再次尝试后续模式)量词。

标签: regex


【解决方案1】:

让我们检查一些回溯定义。

“NFA 引擎的本质是:它考虑每个子表达式或 轮流组件,并且每当需要在两个同样可行的组件之间做出决定时 选项,它会选择一个并记住另一个以稍后返回,如果需要。如果 尝试的选项成功并且正则表达式的其余部分也成功,您是 比赛结束。如果正则表达式其余部分中的任何内容最终导致 失败,正则表达式引擎知道它可以回溯到它选择选项的位置并且 可以通过尝试另一个来继续比赛。这样,它最终会尝试所有 正则表达式的可能排列(或至少与需要一样多,直到匹配 找到)。”Mastering Regular Expressions Powerful Techniques for Perl and Other Tools, Jeffrey E. F. Friedl, p.102

另一个:

“当正则表达式包含可选的量词或交替构造时,输入字符串的评估不再是线性的。与 NFA 引擎的模式匹配由正则表达式中的语言元素驱动,而不是由要匹配的字符驱动在输入字符串中。因此,正则表达式引擎尝试完全匹配可选或替代子表达式。当它前进到子表达式中的下一个语言元素并且匹配不成功时,正则表达式引擎可以放弃其成功匹配的一部分并返回之前保存的状态,以便将正则表达式作为一个整体与输入字符串进行匹配。这个返回之前保存的状态以查找匹配项的过程是称为回溯。” (Backtracking in Regular Expressions, Microsoft docs)

还有一个:

“要匹配一个正则表达式,整个正则表达式必须匹配,而不仅仅是它的一部分。因此,如果包含量词的模式的开头成功导致模式中的后面部分失败,则匹配引擎备份并重新计算开始部分——这就是它被称为回溯的原因。” (source)

似乎回溯的中心部分是“返回”过程到较早的状态以重新评估(重新匹配)字符串以使整个表达式匹配。它不限于如何这样做来调用进程本身。

没有任何来源将回溯限制为仅贪婪或非贪婪量词。您可以在我的 former answer@987654325 中了解贪婪和非贪婪模式行为的差异@。简而言之,它们在机制,返回和重新匹配的方式上有所不同,但本质是相同的,如上述定义所述。

【讨论】:

  • Stribizew 谢谢,它现在理解了,而且我通过阅读一些关于有限状态机的知识来支持我的理解,这让我对正则表达式引擎的工作原理有了更清晰的认识。
猜你喜欢
  • 2019-10-11
  • 2015-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-14
  • 1970-01-01
相关资源
最近更新 更多