我很难理解正则表达式中的一些细微差别。我正在关注教程http://www.regular-expressions.info/backref.html并坚持使用backreferences
匹配打开和关闭代码的示例。
我们有字符串:
Testing <B><I>bold italic</I></B> text
和表达:
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
我可以理解整个逻辑,但无法理解为什么引擎回溯到点:
现在引擎已经到达正则表达式中的第二个
<
了 字符串中的第二个<
。这些匹配。下一个标记是/
。这样做 不匹配I
,引擎被迫回溯到点。点 匹配字符串中的第二个<
。这个明星还是很懒,所以 引擎再次注意到可用的回溯位置和 进展到<
和I
。这些不匹配,所以发动机再次 回溯。
为什么它回溯到点?这是因为我们已成功匹配前一部分的正则表达式,它总是回溯到上一次成功匹配的位置+ 1?
第二部分我无法完全理解。如果我们有一个字符串:
Testing <BOO><I>bold italic</I></B> text
和没有字边界的表达式:
<([A-Z][A-Z0-9]*)[^>]*>.*?</\1>
...在
\1
失败的地方查看正则表达式引擎 第一次。首先,.*?
继续扩展,直到达到。{1}} 字符串的结尾,</\1>
每次.*?
匹配时都无法匹配 还有一个角色。然后正则表达式引擎回溯到捕获组。
[A-Z0-9]*
已与oo
匹配,但与o
或其他任何内容完全匹配。回溯时,[A-Z0-9]*
被迫放弃一个角色。
为什么它会回溯到捕获组而不是像前面的示例那样点?我不明白为什么[A-Z0-9]*
被迫放弃一个角色?是否存在引擎会回溯的一般规则?
答案 0 :(得分:1)
注意 :它不是关于HTML解析,而是使用来自http://regular-expression.info/backref.html的HTML字符串示例深入了解回溯的工作原理的
问题在于我无法理解为什么回溯回滚到特定位置是一般的。
关键是正则表达式引擎试图通过各种方式找到匹配。如果有选项,它可以根据当前模式遵循不同的路径,一旦它找到不匹配的符号,它将尝试它们。见backtracking introduction at rexegg.com:
回溯是现代正则表达式引擎的一个很好的功能:如果一个令牌无法匹配,引擎会回溯到可能采用不同路径的任何位置。然后,贪婪量词可以放弃一个字符,懒惰量词可以扩展以匹配一个,或者可以尝试交替的最右侧。 如果模式继续失败,引擎会系统地探索所有可用路径。
因此,回溯可以回滚到具有量词/变换集的每个构造或分组,以确保在声明匹配失败之前尝试所有可能的组合。您始终回溯到最后匹配的符号的假设是不正确的。
回溯无法访问的唯一地方是原子组或拥有所有权量词的组。此外,环视为零长度的事实会自动使其成为原子(请参阅lookarounds)。
在第一个正则表达式中,\b
标记一个单词边界,因此没有回溯到捕获组中,因为除了已经匹配之外没有其他单词边界。当您删除它时,回溯可以测试捕获组内的所有前面的位置。
要了解回溯和\b
的重要性,请将这些正则表达式与Testing <Boo><I>bold italic</I></Bo> text
输入进行比较:
<([A-Z][A-Z0-9]*)[^>]*>.*?<\/\1o>
- 找不到匹配,因为没有设置字边界,引擎会自由回溯到捕获组,捕获组可能包含B
,Bo
和{{1 }}。<([A-Z][A-Z0-9]*)\b[^>]*>.*?<\/\1o>
- 未找到匹配项,因为第1组只能包含Boo
。