亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Regex > 正文

PHP正則表達式的效率 回溯與固化分組

2020-03-16 21:12:06
字體:
來源:轉載
供稿:網友
上文中,我們聊到了一點關于PHP中(NFA PCRE)正則表達式匹配優先量詞,忽略優先量詞的匹配原理了。那么上文留下的問題,您的答案是什么呢?
 
 
先來看下問題。 

字符串 
復制代碼代碼如下:

$str = '<script>123456</script>'; 

正則表達式為 
復制代碼代碼如下:

$strRegex1 = '%<script>.+<//script>%'; 
$strRegex2 = '%<script>.+?<//script>%'; 
$strRegex3 = '%<script>(?:(?!<//script>).)+<//script>%'; 

這三個正則,分別會造成幾次回溯呢?? 
答案: 
復制代碼代碼如下:

$strRegex1 = '%<script>.+<//script>%'; //9次,記得區別轉義符號。 
$strRegex2 = '%<script>.+?<//script>%'; //5次 
$strRegex3 = '%<script>(?:(?!<//script>).)+<//script>%'; //7次 

對于第一種貪婪匹配的匹配規則,回溯的9次是正則【】對字符串“”匹配時,構成的回溯,回溯的次數,恰好是字符串的長度。 
第二種非貪婪匹配規則,回溯5次,是正則【.+?】對字符串“123456”匹配時構成的回溯?;厮莸拇螖担瑸樽址L度減去最小次數。也就是6-1=5次。如果正則表達式為【.*?】那么,回溯次數就是6次了。 
第三種正則是零寬斷言,或者叫環視。(暫且不說。) 
在NFA正則引擎中,回溯是他的靈魂,所以,不管是貪婪,非貪婪,環視等寫法中肯定會有回溯的出現的,這個我們無法避免(用詞不太準確),但是,我們可以減少回溯的次數,或者保護其中一部分匹配的規則不進行回溯。 

對于上篇BLOG上提到的鳥哥談到一個非貪婪引起的大量回溯問題,大家可以知道,回溯,確實是浪費資源的罪魁禍首,那么,我們能否不讓其回溯呢? 
答案是肯定的,NFA引擎中,有個概念,叫固化分組。引用一下書上的概念 
復制代碼代碼如下:

具體來說,使用「(?>…)」的匹配與正常的匹配并無差別,但是如果匹配進行到此結構之后(也就是,進行到閉括號之后),那么此結構體中的所有備用狀態都會被放棄。也就是說,在固化分組匹配結束時,它已經匹配的文本已經固化為一個單元,只能作為整體而保留或放棄。括號內的子表達式中未嘗試過的備用狀態都不復存在了,所以回溯永遠也不能選擇其中的狀態(至少是,當此結構匹配完成時,“鎖定(locked in)”在其中的狀態)。 

那么,固化分組到底有什么用處呢?我們來舉個例子。(找不到合適的例子,俺只好借用一下書上的例子了) 
比如要處理一批數據,原來格式為123.456,后來因為浮點數顯示問題,部分數據格式變為123.456000000789這種,,要求做到只保留小數點后面2-3位,但是,最后一位不能為0,這個正則如何寫呢?(下面直接考慮小數點后面的數字),寫出正則之后,我們還要用這個正則去匹配數據,把原來的數據替換成匹配的結果。 
首先,我們可以立刻寫出這樣的正則【/./d/d[1-9]?/d*】,PHP代碼為 
復制代碼代碼如下:

$str = preg_replace('/.(/d/d[1-9]?)/d*','//1',$str); //匹配結果的group1進行反向引用 

很明顯,這種寫法,對于部分數據格式為123.456的這種格式,白白的處理了一遍,為了提高效率,我們還要對這個正則進行處理。從123.456這個字符串跟其他的比較一下,我們發現,是疑問123.456這個數據后面沒數字了,所以,白白處理一遍。那好辦,我們對這個正則改造一下,把后面的量詞*改成+,這樣對于123.45 小數點后面1,2位數字的,不會去白白處理,而且,對三位以上數字的,處理正常。其PHP代碼為 
復制代碼代碼如下:

$str = preg_replace('/.(/d/d[1-9]?)/d+','//1',$str); 

好了,這個正則真的沒問題嗎??確定嗎?上篇博文,我們了解了匹配原理,那么,我們也分析一下這個正則的匹配過程吧。 
字符串"123.456",正則表達式為【/.(/d/d[1-9]?)/d+】,我們來看下 
首先(小數點前123不說了),【/.】匹配".",匹配成功,把控制權給下一個【/d】,【/d】匹配“4”成功,把控制權給第二個【/d】,這個【/d】匹配“5”成功,然后,把控制權給了【[1-9]?】,由于量詞是【?】,正則表達式遵循“量詞優先匹配”,而且,此處是【?】,還會留下一個回溯點。然后匹配"6"成功,然后把控制權給【/d+】,【/d+】發現后面沒字符了,最遵循“后進先出”規則,回到上一個回溯點,進行匹配,這時,【[1-9]?】會交還出其匹配的字符“6”,【[1-9]?】匹配“6”成功。匹配完成了。大家發現【(/d/d[1-9]?)】匹配的結果確是"45",并不是我們想要的“456”,“6”被【/d+】匹配去了。那么,我們該如何辦呢? 能否讓【[1-9]?】匹配一旦成功,不進行回溯呢?這就用到了我們上面說的"固化分組", PHP(preg_replace函數)中使用的正則引擎支持固化分組,我們根據固化分組的寫法,可以把代碼改成如下方式 
復制代碼代碼如下:

$str = preg_replace('/.(/d/d(?>[1-9]?))/d+','//1',$str); 

改成這樣的話,那字符串“123.456“是不符合要求,不會被匹配的。那我們就可以實現我們的要求了。 

從上面的例子中,知道了固化分組的作用,那么對于鳥哥BLOG上寫的那個非貪婪的回溯問題,我們能否也對其改造,使得其不回溯呢? 
先看下鳥哥給的答案 
復制代碼代碼如下:

/<script>[^<]*<//script>/is 

鳥哥寫的很精悍。排除“<”之外的所有字符都符合,而且,中間部分不回溯,效率高??墒牵绻虚g有字符“<“的話(如下代碼) 
復制代碼代碼如下:

<script> 
if a < b 
</script> 

那鳥哥的這個正則就不能匹配,就不能實現我們想要的功能了。 
那我們可以根據 固化分組、環視(零寬斷言)來實現這個要求,最后,CFC4N給出的正則以及PHP代碼事例如下
復制代碼代碼如下:

$reg = '%<script>(?>[^<]*)(?>(?!</?script>)<[^<]*)*</script>%is'; 
$str = str_pad("<script>", 111111, "*"); //字符長度大于PHP回溯限制的100000 
$str .= 'if a < b ; if b > c;</script>'; //隨便加幾個包含 < > 的測試字符 
$ret = preg_replace($reg, "OK", $str); 
print_r($ret); //打印結果 OK,證明匹配正確 
var_dump(preg_last_error()); //上一次匹配錯誤。其輸出為 int(0) 

嗨,同學,你看明白了嗎? 

以上為小菜CFC4N的愚文,如有錯誤,歡迎指出。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲精品国产福利| 国产精品久久久久久久天堂| 国产精品成熟老女人| 欧美日韩亚洲网| 国产91网红主播在线观看| 日韩中文字幕第一页| 国产69精品久久久久久| 久久综合电影一区| 免费不卡在线观看av| 97精品在线视频| 成人免费网站在线| 成人亚洲激情网| 在线午夜精品自拍| 国产精品白嫩美女在线观看| 亚洲男女自偷自拍图片另类| 亚洲国产天堂久久国产91| 伊人成人开心激情综合网| 奇米4444一区二区三区| 国产福利精品av综合导导航| 国产精品女主播视频| 日本韩国欧美精品大片卡二| 91人人爽人人爽人人精88v| 欧美日韩福利在线观看| 久久久国产精彩视频美女艺术照福利| 成人国产精品免费视频| 亚洲精品一区av在线播放| 日本久久中文字幕| 国产精品极品美女在线观看免费| 情事1991在线| 亚洲人免费视频| 国产亚洲人成网站在线观看| 国产亚洲精品一区二555| 成人欧美一区二区三区在线湿哒哒| 欧美激情精品久久久久久久变态| 亚洲午夜小视频| 久久国产精品久久国产精品| 亚洲一品av免费观看| 久久99国产综合精品女同| 亚洲美女视频网| 国产日韩欧美影视| 国产精品久久9| 成人a级免费视频| 欧美亚洲视频一区二区| 亚洲视频视频在线| 在线视频欧美性高潮| 国产一区二区三区在线免费观看| 欧美激情区在线播放| 亚洲第一视频在线观看| 一区二区三区精品99久久| 日韩欧美精品中文字幕| 精品高清一区二区三区| 亚洲97在线观看| 欧美高跟鞋交xxxxhd| 91免费精品视频| 91影视免费在线观看| 在线性视频日韩欧美| 亚洲欧美国产制服动漫| 久久av在线播放| 欧美精品www在线观看| 亚洲精品国产免费| 中文字幕欧美精品日韩中文字幕| 日韩亚洲综合在线| 精品国产精品自拍| 日韩电视剧在线观看免费网站| 欧美黄网免费在线观看| 精品欧美一区二区三区| 高清亚洲成在人网站天堂| 亚洲性猛交xxxxwww| 国产欧美日韩中文字幕在线| 成人高h视频在线| 在线观看日韩视频| 久久手机免费视频| 日韩精品高清在线观看| 国产一区二区免费| 久久精品视频中文字幕| 日本精品中文字幕| 亚洲va欧美va国产综合剧情| 国产成人精品a视频一区www| 久久精品影视伊人网| 国产a∨精品一区二区三区不卡| 一个色综合导航| 欧美激情2020午夜免费观看| 国产精品爽爽ⅴa在线观看| 啪一啪鲁一鲁2019在线视频| 亚洲精品国产福利| 欧美不卡视频一区发布| 久久久久久尹人网香蕉| 欧美另类99xxxxx| 高清欧美一区二区三区| 国产91精品久久久| 亚洲视频免费一区| 欧美成人免费va影院高清| 成人精品视频99在线观看免费| 久久久av网站| 亚洲最大中文字幕| 黄色成人在线免费| 欧美大奶子在线| 欧美日韩亚洲激情| 91精品久久久久久久久青青| 亚洲免费视频在线观看| 亚洲久久久久久久久久| 国产成人午夜视频网址| 久久久久久久影院| 国产精品日韩在线观看| 欧美大肥婆大肥bbbbb| 精品久久久久久久久久久| 国产精品高潮在线| 国产精品黄页免费高清在线观看| 久久精品国亚洲| 日韩精品极品视频| 国产亚洲综合久久| 国产成人综合亚洲| 国产精品福利小视频| 久久人人97超碰精品888| 成人网址在线观看| 精品国产欧美一区二区三区成人| 国产精品吹潮在线观看| 欧美最猛黑人xxxx黑人猛叫黄| 91视频免费网站| 国产99久久精品一区二区 夜夜躁日日躁| 久久综合网hezyo| 日韩视频在线免费| 国产日本欧美一区二区三区在线| 欧美大成色www永久网站婷| 日韩美女免费观看| 色av中文字幕一区| 色妞欧美日韩在线| 成人免费网站在线| 欧美精品生活片| 欧美激情久久久久久| 久久久综合免费视频| 中文亚洲视频在线| 欧美精品videos| 国产成+人+综合+亚洲欧美丁香花| 国产精品久久久久久久久久久久| 久久免费精品视频| 欧美日韩中文字幕在线视频| 中文字幕日韩有码| 国产成人在线精品| 国产一区二区在线免费视频| 九九视频直播综合网| 国语自产精品视频在线看| 91在线无精精品一区二区| 国产精品久久久久久久久粉嫩av| 成人网在线观看| 伊人久久免费视频| 精品人伦一区二区三区蜜桃免费| 日韩电影免费在线观看| 亚洲精品美女久久久久| 国产成人av网址| 久久综合伊人77777| 中文字幕一精品亚洲无线一区| 欧美日韩高清在线观看| 亚洲va欧美va在线观看| 亚洲精品在线视频| 国产成人精品免费视频| 久久偷看各类女兵18女厕嘘嘘| 亚洲黄色有码视频| 国产精品成av人在线视午夜片| 最近日韩中文字幕中文| 国产精品国产三级国产aⅴ浪潮| 日韩精品在线观看一区| 国产精品一区二区久久国产| 日本在线精品视频|