在阅读此答案之前,您应该熟悉回溯机制、原子组和所有格量词。您可以在 Friedl 书中找到有关这些概念和功能的信息,并点击以下链接:www.regular-expressions.info、www.rexegg.com
所有测试都是通过全局搜索(使用preg_match_all()
函数)进行的。
(*FAIL)
(或简写(*F)
)
baabo caaco daado
caac(*FAIL)|aa.|caaco|co
[0] => aab
[1] => caaco
[2] => aad
(*FAIL)
导致与模式中的“坏字符”完全相同的行为。例如,如果您将其替换为“R”,您将获得完全相同的结果:caacR|aa.|caaco|co
. 更笼统地说,您确实可以(*FAIL)
用“总是失败的子模式”代替:(?!)
, (?=a(?<!a))
,...
a (first from "baabo")
: 不出所料,第一个结果是由第二种选择找到的。( aab
)
c (first)
:正则表达式引擎遇到第一个“c”并尝试第一个替代方案并找到:caac
,但子模式被迫失败。然后正则表达式引擎(总是从第一个“c”开始)尝试第二个失败的替代方案,第三个替代方案成功。( caaco
)
a (first from "daado")
: 第三个结果是由第二种选择找到的。( aad
)
(*SKIP)
baabo caaco daado
caa(*SKIP)c(*FAIL)|aa.|caaco|co
[0] => aab
[1] => co
[2] => aad
这个动词定义了一个点,当子模式稍后失败时,正则表达式引擎不允许回溯。因此,之前使用子模式找到的所有字符都被一次性使用,并且不能用于模式的另一部分(替代方案)。
a (first from "baabo")
: 第一个结果是由第二个备选方案找到的。( aab
)
c (first)
:正则表达式引擎caac
在第一种情况下找到,然后失败((*FAIL)
动词的原因),回溯到第二个“c”,但不允许回溯到(*SKIP)
动词之前先前匹配的字符(“caa”)。
c (second)
:现在,正则表达式引擎总是尝试第一个替代方案,但在这个新位置并失败,因为后面有一个“o”而不是“a”,然后它回溯到第二个“c”。请注意,在这种情况下,这些字符不会像以前那样被消耗,因为子模式之前未能到达(*SKIP)
动词。第二种选择经过测试并失败(不以“c”开头)。第三种选择也失败了,因为下一个字符是“o”而不是“a”。第四种选择成功并给出了第二个结果。co
( )
a (first from "daado")
: 第三个结果是由第二种选择找到的。( aad
)
(*PRUNE)
baabo caaco daado
caa(*PRUNE)c(*FAIL)|aa.|caaco|co
[0] => aab
[1] => aac
[2] => aad
这个动词不同,(*SKIP)
因为它不禁止使用所有先前匹配的字符,但如果子模式稍后将失败,则跳过子模式的第一个匹配字符(或禁止子模式开始)。
a (first from "baabo")
: 第一个结果是由第二个备选方案找到的。( aab
)
c (first)
:正则表达式引擎caac
在第一种情况下找到,然后失败,但现在从“caaco”回溯到第一个“a”,因为第一个“c”被跳过。
a (first from "caaco")
: 第一个选项被尝试并失败,第二个成功并给出第二个结果。( aac
)
a (first from "daado")
: 第三个结果是由第二种选择找到的。( aad
)