放了两天后,才灵机一触,想通了问题的关键所在,下面将展开讨论。
在此之前,可能还需要做两点说明:
1、 下面讨论的话题已经与本文开始提到的问题没有多大关联了,最初的问题主要是为了引出本文的话题,问题本身不在讨论范围之内,而本文也主要是纯理论的探讨。
2、 本文适合有一定正则基础的读者。如果您对上面几个正则的匹配结果和匹配过程感到费解,没关系,下面就将为您解惑;但是如果您对上面几个正则中元字符和语法代表的意义都不清楚的话,还是先从基础看起吧。
2 逆序环视匹配原理深入
正则表达式一:(?<=(\d)\d*)\1
正则表达式二:(?<=(\d)\d*?)\1
正则表达式三:(?<=(\d))\d*\1
正则表达式四:(?<=(\d))\d*?\1
上面的几个正则表达式,可以最终抽象为“(?<=SubExp1)SubExp2”这样的表达式,在做逆序环视原理分析时,根据“SubExp1”的特点,可以归纳为三类:
1、 逆序环视中的子表达式“SubExp1”长度固定,正则表达式三和四属于这一类,当然,这一类里是包括“?”这一量词的,但也仅限于这一个量词。
2、 逆序环视中的子表达式“SubExp1”长度不固定,其中包含忽略优先量词,如“*?”、“+?”、“{m,}?”等,也就是通常所说的非贪婪模式,正则表达式二属于这一类。
3、 逆序环视中的子表达式“SubExp1”长度不固定,其中包含匹配优先量词,“*”、“+”、“{m,}”等,也就是通常所说的贪婪模式,正则表达式一属于这一类。
下面针对这三类正则表达式进行匹配过程的分析。
2.1 固定长度子表达式匹配过程分析
2.1.1 源字符串一 + 正则表达式三匹配过程
源字符串一:878
正则表达式三:(?<=(\d))\d*\1
首先在位置0处开始尝试匹配,由“(?<=(\d))”取得控制权,长度固定,只有一位,由位置0处向左查找一位,失败,“(?<=(\d))”匹配失败,导致第一轮匹配尝试失败。
正则引擎传动装置向前传动,由位置1处尝试匹配,控制权交给“(?<=(\d))”,向左查找一位,接着将控制权交给“(\d)”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,此时“(?<=(\d))”匹配成功,匹配结果为位置1,捕获组1匹配到的内容就是“8”,控制权交给“\d*”。由于“\d*”为贪婪模式,会优先尝试匹配位置1后面的“7”和“8”,匹配成功,记录回溯状态,控制权交给“\1”。由于前面捕获组1捕获到的内容是“8”,所以“\1”要匹配到“8”才能匹配成功,而此时已到达字符串结尾处,匹配失败,“\d*”回溯,让出最后的字符“8”,再将控制权交给“\1”, 由“\1”匹配最后的“8”成功,此时整个表达式匹配成功。由于“(?<=(\d))”只匹配位置,不占有字符,所以整个表达式匹配到的结果为“78”,其中“\d*”匹配到的是“7”,“\1”匹配到的是“8”。
2.1.2 源字符串二 + 正则表达式三匹配过程
源字符串二:9878
正则表达式三:(?<=(\d))\d*\1
这一组合的匹配过程,与2.1.1节的匹配过程基本类似,只不过多了一轮匹配尝试而已,这里不再赘述。
2.1.3 源字符串一 + 正则表达式四匹配过程
源字符串一:878
正则表达式四:(?<=(\d))\d*?\1
首先在位置0处开始尝试匹配,由“(?<=(\d))”取得控制权,长度固定,只有一位,由位置0处向左查找一位,失败,“(?<=(\d))”匹配失败,导致第一轮匹配尝试失败。
正则引擎传动装置向前传动,由位置1处尝试匹配,控制权交给“(?<=(\d))”,向左查找一位,接着将控制权交给“(\d)”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,此时“(?<=(\d))”匹配成功,匹配结是果为位置1,捕获组1匹配到的内容就是“8”,控制权交给“\d*?”。由于“\d*?”为非贪婪模式,会优先尝试忽略匹配,记录回溯