正则表达式的固化分组
固化分組,又叫原子組。
語(yǔ)法格式:(?>…)
我們?cè)谑褂梅秦澙纺J綍r(shí),匹配過(guò)程中可能會(huì)進(jìn)行多次的回溯,回溯越多,正則表達(dá)式的運(yùn)行效率就越低。而固化分組就是用來(lái)減少回溯次數(shù)的。
實(shí)際上,固化分組 (?>…) 的匹配與正常的匹配并無(wú)分別,它并不會(huì)改變匹配結(jié)果。唯一的不同就是:固化分組匹配結(jié)束時(shí),它匹配到的文本已經(jīng)固化為一個(gè)單元,只能作為整體而保留或放棄,小括號(hào)內(nèi)的子表達(dá)式中未使用的備用狀態(tài)都會(huì)被放棄,所以回溯永遠(yuǎn)也不能選擇其中的狀態(tài)(因此不能參與回溯)。
javaScript,java 和 python 中并不支持固化分組的語(yǔ)法。不過(guò),它在 php 和 .NET 中表現(xiàn)良好。
下面我們通過(guò)一個(gè) php 的固化分組正則表達(dá)式例子來(lái)深入地理解固化分組:
假如要處理一批數(shù)據(jù),原格式為 123.456, 因?yàn)楦↑c(diǎn)數(shù)顯示問(wèn)題,部分?jǐn)?shù)據(jù)格式會(huì)變?yōu)?123.456000000789 這種, 現(xiàn)要求只保留小數(shù)點(diǎn)后 2~3 位,但是最后一位不能為 0,那么這個(gè)正則怎么寫(xiě)呢?
$str = "123.456000000789"; echo preg_replace("/(\.\d\d[1-9]?)\d*/","\\1",$str); // 123.456以上的正則表達(dá)式,對(duì)于 123.456 這種格式的數(shù)據(jù),將白白處理一遍,為了提高效率,我們應(yīng)該將 123.456 排除在外,我們將量詞 * 改成 +。如下所示:
$str = "123.456"; echo preg_replace("/(\.\d\d[1-9]?)\d+/","\\1",$str);(.\d\d[1-9]?) 會(huì)正常匹配到 .456,而 \d+ 表示匹配大于等于 1 個(gè)由 0-9 的任意數(shù)字組成的字符串,即匹配至少一個(gè)任意數(shù)字,所以 \d+ 匹配到 6 后面的結(jié)尾處位置時(shí),會(huì)匹配失敗,然后會(huì)要求 [1-9]? 回溯到備選狀態(tài),也就是放棄匹配數(shù)字 6,讓給 \d+ 去匹配,結(jié)果匹配成功,最后整個(gè)正則式匹配到字符串 .456,(.\d\d[1-9]?) 匹配到 .45,\d+ 匹配到 6,所以 \1 引用到的是 .45,也就是把源字符串 123.456,后面的 .456 替換成了 .45,本來(lái)保留小數(shù)點(diǎn)后 3 位是符合需求的,現(xiàn)在反而被處理成了 .45,顯然 123.45 不是我們期望的匹配結(jié)果,那我們應(yīng)該怎么做呢?能否讓 [1-9]? 一旦匹配成功,便不再進(jìn)行回溯,這里就要用到固化分組,如下所示:
$str = "123.456"; echo preg_replace("/(\.\d\d(?>[1-9]?))\d+/","\\1",$str);(?>[1-9]?) 就是固化分組,(?>[1-9]?) 成功匹配了 6,因?yàn)?(?>[1-9]?) 是固化分組,所以 ? 產(chǎn)生的備選狀態(tài)被放棄,此時(shí)固化分組匹配到的 6 便不能用于正則引擎的回溯。子表達(dá)式 \d+ 匹配失敗,嘗試回溯也失敗,導(dǎo)致整個(gè)正則表達(dá)式第 1 次迭代匹配失敗,后面會(huì)繼續(xù)迭代匹配到源字符串的結(jié)尾處,最后正則引擎才停止服務(wù),不過(guò)后面的幾次迭代匹配也都是失敗的,那么最終的結(jié)果就是,正則表達(dá)式?jīng)]有匹配到任何東西,替換動(dòng)作也沒(méi)有執(zhí)行,這符合我們的需求。
php 還提供了占有優(yōu)先的語(yǔ)法,效果和固化分組一樣:
$str = "123.456"; echo preg_replace("/(\.\d\d[1-9]?+)\d+/","\\1",$str);在量詞 ? 后面加個(gè) +,就變成了占有優(yōu)先量詞,意為著 [1-9]?+ 匹配到 6 后,是沒(méi)有備選狀態(tài)的(即無(wú)法回溯),這樣 \d+ 最后匹配失敗,最終導(dǎo)致整個(gè)匹配過(guò)程失敗,沒(méi)有匹配到任何東西。
雖然 java 不支持固化分組的語(yǔ)法,但 java 也提供了占有優(yōu)先的語(yǔ)法,同樣能夠避免正則回溯。如下:
String str = "123.456"; System.out.println(str.replaceAll("(\\.\\d\\d[1-9]?+)\\d+", "$1"));// 123.456值得注意的是:java 中 replaceAll 方法需要雙反斜杠 \\作為轉(zhuǎn)義符號(hào)。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的正则表达式的固化分组的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 超前钻是什么意思 超前钻是啥意思
- 下一篇: 正则表达式的环视实际应用案例