用环视实现固化分组
首先看这篇博文的“老几”应该知道固化分组
的概念。但是也有可能是读者冲着环视进来的。所以还是有必要的说一下固化分组
的概念。
咱知道,NFA 的灵魂是回溯,固化分组
也是 NFA 特有的,而固化分组
的任务却是让你丢弃前面的备用状态。那固化分组的作用是什么呢?
想想你有没有这样的需求,如果你写了这样一个正则
[0-3]?\d
去匹配“3
”这个字符串
毫无疑问,3
应该是被\d
匹配到了,因为[0-3]?
是匹配优先的,于是,先是被[0-3]?
匹配到,然后发现\d
不能被匹配,于是回溯,控制权转移到\d
,然后由\d
匹配到3
,但是我们的需求是让[0-3]?
去匹配到,而不是让\d
去匹配到,这时候就可以用固化分组,把原先的正则改为
(?>[0-3]?)\d
这时候[0-3]?
就能匹配到3
了,如果你的正则引擎不支持固化分组,没关系,这篇博文的目的就是可以用环视来实现一个固化分组。
想想,其实固化分组
和占有量词非常非常的相似,占有量词是指++
, ?+
, *+
之类的东西,这些量词一旦匹配到了就不会交出已经匹配的字符了,换一种说法,它会丢弃之前已经保存的备用状态。固化分组
也是一样的,它也会将匹配到的留给自己,不会交出任何一个字符。
好了,回到正题,我们如何用环视实现一个固化分组
,还记得环视相关的知识吗?环视不明确匹配字符,而是匹配一个位置!那这样就好办了啊,比如我们想要固化的是3
这个子串,并且想要它为可选的
我们先用环视匹配这个子串左边的位置
(?=3?)
看起来好像没有一点问题,其实这时候3
已经被固化了,因为没有保存任何的备用状态,但是这样的话有个问题。
我们仅仅是匹配位置,所以下次开始匹配的位置还是在原地,我们得匹配点子串,让位置前移才行,于是改进之后
(?=(3?))\1
捕获组捕获到3?
之后反向引用。
接下来看个新例子,现在我们把正则表达式这样写:
(?=([0-4]?))\1\d
然后去匹配3
,哈,你发现匹配失败了,这就是我们想要的结果,3
被[0-4]?
匹配了!!