定义
占有优先量词:
?+ *+ ++ {m,n}+
占有优先量词与匹配优先量词很相似,只是它们从来不会交还已经匹配的字符。
固化分组:
(?>...) ...是指具体内容
固化分组的内容与正常的匹配并无区别,只是当匹配完括号中的内容后,括号中的备用状态会全部舍去。
例子
将所有的小数保留三位,规则如下:如果小数位数第三位不为0保留三位,如果小数位数少于三位或第三位为0,保留两位小数。
首先可以用匹配优先量词去做:
在Mac终端下运行如下命令:
perl -p -i -e 's/(\.\d\d[1-9]?)\d*/$1/g' test.txt
运行前test.txt中的内容如下:
运行后:
这样做肯定是没问题的,但是在处理最后一个数字1.345时,正则表达式相当于是先匹配了’.345’,然后又用’.345’去替换,白费功夫。如何才能避免这种情况,使其更加高效呢?这就可以用占有优先量词和固化分组了。
占有优先量词:
perl -p -i -e 's/(\.\d\d[1-9]?+)\d+/$1/g' test.txt
当正则表达式匹配到’1.345’会怎样呢?首先匹配前面的‘.34’,匹配到’5’时,因为是匹配优先,所以‘[1-9]?+’会先匹配’5’,又因为是占有优先,所以当后面的’\d+’匹配不成功时,不会交还字符,从而导致匹配失败,就不会替换,这正是我们想要的效果。
固化分组:
(\.\d\d(?>[1-9]?))\d+
同样的道理,当匹配到’5’时,因为是匹配优先,所以‘(?>[1-9]?)’会匹配‘5’,有因为是固化分组,所以当后面的’\d+’匹配不成功时,不会交还字符,从而导致匹配失败。
用肯定环视模拟固化分组
有些流派并不支持占有优先量词和固化分组,比如JavaScript,这是我们就可以用肯定环视来模拟固化分组:
.\d\d(?=([1-9]?))\1\d+
环视结构也有备用状态,当环视结构匹配完后,环视结构就会抛弃其内部的备用状态:
首先来看第一个数字,当匹配到‘.23’后,开始匹配环视结构内部,因为‘[1-9]?’是匹配优先,所以会先匹配‘4’,此时环视结构内部有一个备用状态,就是不匹配任何字符。继续往后,环视结构匹配结束,抛弃环视结构内部的那个备用状态,继续往后匹配。因为环视结构是不匹配任何字符的,它只是看一下后面是不是要匹配的内容,所以用反向引用‘\1’来匹配刚刚环视结构里的内容,‘\d+’匹配后面的‘980’。
当匹配第二个数字时,也是先匹配到‘.23’,开始匹配环视结构内部,因为‘[1-9]?’是匹配优先,所以会尝试匹配‘0’,此时它有一个备用状态,就是不匹配任何字符。发现无法匹配‘0’,启用备用状态,就是什么都不匹配,环视结构匹配成功,因为环视结构内部没有匹配到任何东西,多以反向引用‘\1’也不会匹配任何字符,‘\d+’匹配后面的‘00293’。