递归模式

递归模式


对于程序员来说,递归函数肯定都不陌生。函数体内再次调用该函数,只要是函数执行到这个点,就再次调用自己,从而进入函数执行,如果没有条件让函数返回的话,那么函数会无限次的调用自己,从而造成死循环。当然了,好的递归函数能帮助我们少写很多代码。也非常容易让别人理解。

对于正则表达式的递归模式原理其实和递归函数是相同的。它的形式为(?R),表示无限递归。当正则引擎走到(?R)这个点时,就会从正则表达式的开始进行匹配。当然,在正则表达式匹配的过程中,在某些条件下正则引擎也会从正则表达式的开始部分重新进行匹配。但是和递归模式不同的是,那是在某个正则匹配失败,并且有其他的可选情况时才会去重新匹配。而使用递归模式是在正常匹配成功下也可以重新进入。

现在我们通过一个例子,说一下递归模式的遍历过程。/a(?R)?c/ 可以匹配 "aaaccc",其中'a''c'的个数是任意的。我们就以"aaaccc"为例来介绍。首先正则a 和第一个字符'a'匹配成功。进行下一个正则,由于是递归模式,所以进入第一层递归,从开始的正则a进行匹配,和第二个字符'a'匹配成功,然后再进行一下个正则,发现还是递归,于是继续进入第二层递归,从开始的正则a进行匹配,这次是和第三个字符'a'进行匹配,可以匹配成功。然后继续下一个正则,还是递归,于是进入第三层递归,依然是从开始的正则a进行匹配,此时的字符我们就以第一个'c'来称呼,匹配失败。由于没有可选路径,正则表达式匹配失败。从第三层退出,也就是相当于(?R)失败了,又因为?使(?R)是可选的,所以可以继续下一个正则c和此时的第一个'c'匹配,匹配成功。此时相当于第二层的正则已经匹配成功了,继续退出第二层,此时第一层的(?R)匹配成功,继续下一个正则c和第二个'c'匹配,匹配成功。继续退出第一层递归,此时回到最开始的正则,(?R)匹配成功,继续下一个正则c和第三个'c'匹配,很明显可以匹配成功。此时整个正则表达式已经匹配完成了。

注意,上面例子的正则表达式,(?R)必须是一个可选的正则,也就是后面要跟着?或者*。这是给递归一个正确的结束条件,否则的话,递归无法正常结束。现在我们不加?或者*,对于正则表达式/a(?R)c/匹配"aaaccc",看看会 发生什么情况。进入递归的过程我们不再赘述,现在我们直接看在第三层递归的时候,正则a和目标字符串中的第一个'c'匹配失败,由于没有可选项,所以第三层的正则表达式匹配失败。退出第三层回到第二层,因为第三层是失败退出,所以相当于第二层的(?R)匹配失败了,但是我们看,这里没有?或者*量词,所以它不是可选的。(?R)失败,但是也没有其他可选项,所以第二层的正则表达式也匹配失败。就会退出第二层的递归,回到第一层。同样第二层也是以失败退出,所以第一层的(?R)也是匹配失败,没有可选项,所以继续退出第一层递归。来到最初的地方,也是一个失败。同样因为(?R)不是可选的,所以继续失败,从而整个正则表达式匹配失败。每一层(?R)后面的正则c都不能拿来匹配了,你说这是不是很遗憾。

有一个比较经典的括号配对的问题,"((()))" 这是一个完整的配对的字串;((()这是不完全配对的。如果没有递归模式,正则表达式要想匹配出其中成对的还真是不容易。然而递归模式可以很容的解决这个问题。/\((?R)?\)/就可以将其中的成对的括号匹配出来。

除了使用(?R)进行无限递归之外,还可以使用(?数字)的形式指定递归几层。例如:(?1)(?3)等。

查看笔记

扫码一下
查看教程更方便