麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > Regex > 正文

正則表達式之 貪婪與非貪婪模式詳解(概述)

2020-03-16 21:09:05
字體:
來源:轉載
供稿:網友
網上找到的貪婪與非貪婪模式詳解,看了這一段基本明白貪婪與非貪婪模式的構成條件
 
 
1 概述 
貪婪與非貪婪模式影響的是被量詞修飾的子表達式的匹配行為,貪婪模式在整個表達式匹配成功的前提下,盡可能多的匹配,而非貪婪模式在整個表達式匹配成功的前提下,盡可能少的匹配。非貪婪模式只被部分NFA引擎所支持。 

屬于貪婪模式的量詞,也叫做匹配優先量詞,包括: 

“{m,n}”、“{m,}”、“?”、“*”和“+”。 

在一些使用NFA引擎的語言中,在匹配優先量詞后加上“?”,即變成屬于非貪婪模式的量詞,也叫做忽略優先量詞,包括: 

“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。 

從正則語法的角度來講,被匹配優先量詞修飾的子表達式使用的就是貪婪模式,如“(Expression)+”;被忽略優先量詞修飾的子表達式使用的就是非貪婪模式,如“(Expression)+?”。 

對于貪婪模式,各種文檔的叫法基本一致,但是對于非貪婪模式,有的叫懶惰模式或惰性模式,有的叫勉強模式,其實叫什么無所謂,只要掌握原理和用法,能夠運用自如也就是了。個人習慣使用貪婪與非貪婪的叫法,所以文中都會使用這種叫法進行介紹。 

2 貪婪與非貪婪模式匹配原理 
對于貪婪與非貪婪模式,可以從應用和原理兩個角度進行理解,但如果想真正掌握,還是要從匹配原理來理解的。 

先從應用的角度,回答一下“什么是貪婪與非貪婪模式?” 

2.1 從應用角度分析貪婪與非貪婪模式 
2.1.1 什么是貪婪與非貪婪模式 
先看一個例子 

舉例: 

源字符串:aa<div>test1</div>bb<div>test2</div>cc 

正則表達式一:<div>.*</div> 

匹配結果一:<div>test1</div>bb<div>test2</div> 

正則表達式二:<div>.*?</div> 

匹配結果二:<div>test1</div>(這里指的是一次匹配結果,所以沒包括<div>test2</div>) 

根據上面的例子,從匹配行為上分析一下,什是貪婪與非貪婪模式。 

正則表達式一采用的是貪婪模式,在匹配到第一個“</div>”時已經可以使整個表達式匹配成功,但是由于采用的是貪婪模式,所以仍然要向右嘗試匹配,查看是否還有更長的可以成功匹配的子串,匹配到第二個“</div>”后,向右再沒有可以成功匹配的子串,匹配結束,匹配結果為“<div>test1</div>bb<div>test2</div>”。當然,實際的匹配過程并不是這樣的,后面的匹配原理會詳細介紹。 

僅從應用角度分析,可以這樣認為,貪婪模式,就是在整個表達式匹配成功的前提下,盡可能多的匹配,也就是所謂的“貪婪”,通俗點講,就是看到想要的,有多少就撿多少,除非再也沒有想要的了。 

正則表達式二采用的是非貪婪模式,在匹配到第一個“</div>”時使整個表達式匹配成功,由于采用的是非貪婪模式,所以結束匹配,不再向右嘗試,匹配結果為“<div>test1</div>”。 

僅從應用角度分析,可以這樣認為,非貪婪模式,就是在整個表達式匹配成功的前提下,盡可能少的匹配,也就是所謂的“非貪婪”,通俗點講,就是找到一個想要的撿起來就行了,至于還有沒有沒撿的就不管了。 

2.1.2 關于前提條件的說明 
在上面從應用角度分析貪婪與非貪婪模式時,一直提到的一個前提條件就是“整個表達式匹配成功”,為什么要強調這個前提,我們看下下面的例子。 

正則表達式三:<div>.*</div>bb 

匹配結果三:<div>test1</div>bb 

修飾“.”的仍然是匹配優先量詞“*”,所以這里還是貪婪模式,前面的“<div>.*</div>”仍然可以匹配到“<div>test1</div>bb<div>test2</div>”,但是由于后面的“bb”無法匹配成功,這時“<div>.*</div>”必須讓出已匹配的“bb<div>test2</div>”,以使整個表達式匹配成功。這時整個表達式匹配的結果為“<div>test1</div>bb”,“<div>.*</div>”匹配的內容為“<div>test1</div>”。可以看到,在“整個表達式匹配成功”的前提下,貪婪模式才真正的影響著子表達式的匹配行為,如果整個表達式匹配失敗,貪婪模式只會影響匹配過程,對匹配結果的影響無從談起。 

非貪婪模式也存在同樣的問題,來看下面的例子。 

正則表達式四:<div>.*?</div>cc 

匹配結果四:<div>test1</div>bb<div>test2</div>cc 

這里采用的是非貪婪模式,前面的“<div>.*?</div>”仍然是匹配到“<div>test1</div>”為止,此時后面的“cc”無法匹配成功,要求“<div>.*?</div>”必須繼續向右嘗試匹配,直到匹配內容為“<div>test1</div>bb<div>test2</div>”時,后面的“cc”才能匹配成功,整個表達式匹配成功,匹配的內容為“<div>test1</div>bb<div>test2</div>cc”,其中“<div>.*?</div>”匹配的內容為“<div>test1</div>bb<div>test2</div>”。可以看到,在“整個表達式匹配成功”的前提下,非貪婪模式才真正的影響著子表達式的匹配行為,如果整個表達式匹配失敗,非貪婪模式無法影響子表達式的匹配行為。 

2.1.3 貪婪還是非貪婪——應用的抉擇 
通過應用角度的分析,已基本了解了貪婪與非貪婪模式的特性,那么在實際應用中,究竟是選擇貪婪模式,還是非貪婪模式呢,這要根據需求來確定。 

對于一些簡單的需求,比如源字符為“aa<div>test1</div>bb”,那么取得div標簽,使用貪婪與非貪婪模式都可以取得想要的結果,使用哪一種或許關系不大。 

但是就2.1.1中的例子來說,實際應用中,一般一次只需要取得一個配對出現的div標簽,也就是非貪婪模式匹配到的內容,貪婪模式所匹配到的內容通常并不是我們所需要的。 

那為什么還要有貪婪模式的存在呢,從應用角度很難給出滿意的解答了,這就需要從匹配原理的角度去分析貪婪與非貪婪模式。 

2.2 從匹配原理角度分析貪婪與非貪婪模式 
如果想真正了解什么是貪婪模式,什么是非貪婪模式,分別在什么情況下使用,各自的效率如何,那就不能僅僅從應用角度分析,而要充分了解貪婪與非貪婪模式的匹配原理。 

2.2.1 從基本匹配原理談起 
NFA引擎基本匹配原理參考:正則基礎之——NFA引擎匹配原理。 

這里主要針對貪婪與非貪婪模式涉及到的匹配原理進行介紹。先看一下貪婪模式簡單的匹配過程。 

源字符串:"Regex" 

正則表達式:".*" 
正則表達式之 貪婪與非貪婪模式詳解(概述)

圖2-1 

注:為了能夠看清晰匹配過程,上面的空隙留得較大,實際源字符串為“”Regex””,下同。 

來看一下匹配過程。首先由第一個“"”取得控制權,匹配位置0位的“"”,匹配成功,控制權交給“.*”。 

“.*”取得控制權后,由于“*”是匹配優先量詞,在可匹配可不匹配的情況下,優先嘗試匹配。從位置1處的“R”開始嘗試匹配,匹配成功,繼續向右匹配,匹配位置2處的“e”,匹配成功,繼續向右匹配,直到匹配到結尾的“””,匹配成功,由于此時已匹配到字符串的結尾,所以“.*”結束匹配,將控制權交給正則表達式最后的“"”。 

“"”取得控制權后,由于已經在字符串結束位置,匹配失敗,向前查找可供回溯的狀態,控制權交給“.*”,由“.*”讓出一個字符,也就是字符串結尾處的“””,再把控制權交給正則表達式最后的“"”,由“"”匹配字符串結尾處的“"”,匹配成功。 

此時整個正則表達式匹配成功,其中“.*”匹配的內容為“Regex”,匹配過程中進行了一次回溯。 

接下來看一下非貪婪模式簡單的匹配過程。 

源字符串:"Regex" 

正則表達式:".*?" 

正則表達式之 貪婪與非貪婪模式詳解(概述)


圖2-2 

看一下非貪婪模式的匹配過程。首先由第一個“"”取得控制權,匹配位置0位的“"”,匹配成功,控制權交給“.*?”。 

“.*?”取得控制權后,由于“*?”是忽略優先量詞,在可匹配可不匹配的情況下,優先嘗試不匹配,由于“*”等價于“{0,}”,所以在忽略優先的情況下,可以不匹配任何內容。從位置1處嘗試忽略匹配,也就是不匹配任何內容,將控制權交給正則表達式最后的“””。 

“"”取得控制權后,從位置1處嘗試匹配,由“"”匹配位置1處的“R”,匹配失敗,向前查找可供回溯的狀態,控制權交給“.*?”,由“.*?”吃進一個字符,匹配位置1處的“R”,再把控制權交給正則表達式最后的“"”。 

“"”取得控制權后,從位置2處嘗試匹配,由“"”匹配位置1處的“e”,匹配失敗,向前查找可供回溯的狀態,重復以上過程,直到由“.*?”匹配到“x”為止,再把控制權交給正則表達式最后的“"”。 

“"”取得控制權后,從位置6處嘗試匹配,由“"”匹配字符串最后的“"”,匹配成功。 

此時整個正則表達式匹配成功,其中“.*?”匹配的內容為“Regex”,匹配過程中進行了五次回溯。 

2.2.2 貪婪還是非貪婪——匹配效率的抉擇 
通過匹配原理的分析,可以看到,在匹配成功的情況下,貪婪模式進行了更少的回溯,而回溯的過程,需要進行控制權的交接,讓出已匹配內容或匹配未匹配內容,并重新嘗試匹配,在很大程度上降低匹配效率,所以貪婪模式與非貪婪模式相比,存在匹配效率上的優勢。 

但2.2.1中的例子,僅僅是一個簡單的應用,讀者看到這里時,是否會存在這樣的疑問,貪婪模式就一定比非貪婪模式匹配效率高嗎?答案是否定的。 

舉例: 

需求:取得兩個“"”中的子串,其中不能再包含“"”。 

正則表達式一:".*" 

正則表達式二:".*?" 

情況一:當貪婪模式匹配到更多不需要的內容時,可能存在比非貪婪模式更多的回溯。比如源字符串為“The word "Regex" means regular expression.”。 

情況二:貪婪模式無法滿足需求。比如源字符串為“The phrase "regular expression" is called "Regex" for short.”。 

對于情況一,正則表達式一采用的貪婪模式,“.*”會一直匹配到字符串結束位置,控制權交給最后的“””,匹配不成功后,再進行回溯,由于多匹配的內容“means regular expression.”遠遠超過需匹配內容本身,所以采用正則表達式一時,匹配效率會比使用正則表達式二的非貪婪模式低。 

對于情況二,正則表達式一匹配到的是“"regular expression" is called "Regex"”,連需求都不滿足,自然也談不上什么匹配效率的高低了。 

以上兩種情況是普遍存在的,那么是不是為了滿足需求,又兼顧效率,就只能使用非貪婪模式了呢?當然不是,根據實際情況,變更匹配優先量詞修飾的子表達式,不但可以滿足需求,還可以提高匹配效率。 

源字符串:"Regex" 

給出正則表達式三:"[^"]*" 

看一下正則表達式三的匹配過程。 
正則表達式之 貪婪與非貪婪模式詳解(概述)

圖2-3 

首先由第一個“"”取得控制權,匹配位置0位的“"”,匹配成功,控制權交給“[^"]*”。 

“[^"]*”取得控制權后,由于“*”是匹配優先量詞,在可匹配可不匹配的情況下,優先嘗試匹配。從位置1處的“R”開始嘗試匹配,匹配成功,繼續向右匹配,匹配位置2處的“e”,匹配成功,繼續向右匹配,直到匹配到“x”,匹配成功,再匹配結尾的“””時,匹配失敗,將控制權交給正則表達式最后的“"”。 

“””取得控制權后,匹配字符串結尾處的“””,匹配成功。 

此時整個正則表達式匹配成功,其中“[^"]*”匹配的內容為“Regex”,匹配過程中沒有進行回溯。 

將量詞修飾的子表達式由范圍較大的“.”,換成了排除型字符組“[^"]”,使用的仍是貪婪模式,很完美的解決了需求和效率問題。當然,由于這一匹配過程沒有進行回溯,所以也不需要記錄回溯狀態,這樣就可以使用固化分組,對正則做進一步的優化。 

給出正則表達式四:"(?>[^"]*)" 

固化分組并不是所有語言都支持的,如.NET支持,而Java就不支持,但是在Java中卻可以使用更簡單的占有優先量詞來代替:"[^"]*+"。 

3 貪婪還是非貪婪模式——再談匹配效率 
一般來說,貪婪與非貪婪模式,如果量詞修飾的子表達式相同,比如“.*”和“.*?”,它們的應用場景通常是不同的,所以效率上一般不具有可比性。 

而對于改變量詞修飾的子表達式,以滿足需求時,比如把“.*”改為“[^"]*”,由于修飾的子表達式已不同,也不具有直接的可對比性。但是在相同的子表達式,又都可以滿足需求的情況下,比如“[^"]*”和“[^"]*?”,貪婪模式的匹配效率通常要高些。 

同時還有一個事實就是,非貪婪模式可以實現的,通過優化量詞修飾的子表達式的貪婪模式都可以實現,而貪婪模式可以實現的一些優化效果,卻未必是非貪婪模式可以實現的。 

貪婪模式還有一點優勢,就是在匹配失敗時,貪婪模式可以更快速的報告失敗,從而提升匹配效率。下面將全面考察貪婪與非貪婪模式的匹配效率。 

3.1 效率提升——演進過程 
在了解了貪婪與非貪婪模式的匹配基本原理之后,我們再來重新看一下正則效率提升的演進過程。 

需求:取得兩個“"”中的子串,其中不能再包含“"”。 

源字符串:The phrase "regular expression" is called "Regex" for short. 

正則表達式一:".*" 

正則表達式一匹配的內容為“"regular expression" is called "Regex"”,不符合要求。 

提出正則表達式二:".*?" 

首先“"”取得控制權,由位置0位開始嘗試匹配,直到位置11處匹配成功,控制權交給“.*?”,匹配過程同2.2.1中非貪婪模式的匹配過程。“.*?”匹配的內容為“Regex”,匹配過程中進行了四次回溯。 

如何消除回溯帶來的匹配效率的損失,就是使用更小范圍的子表達式,采用貪婪模式,提出正則表達式三:"[^"]*" 

首先“"”取得控制權,由位置0位開始嘗試匹配,直到位置11處匹配成功,控制權交給“[^"]*”,匹配過程同2.2.2節中非貪婪模式的匹配過程。“[^"]*”匹配的內容為“Regex”,匹配過程中沒有進行回溯。 

3.2 效率提升——更快的報告失敗 
以上討論的是匹配成功的演進過程,而對于一個正則表達式,在匹配失敗的情況下,如果能夠以最快的速度報告匹配失敗,也會提升匹配效率,這或許是我們設計正則過程中最容易忽略的。而在源字符串數據量非常大,或正則表達式比較復雜的情況下,是否能夠快速報告匹配失敗,將對匹配效率產生直接的影響。 

下面將構建匹配失敗的正則表達式,對匹配過程進行分析。 

以下匹配過程分析中,源字符串統一為:The phrase "regular expression" is called "Regex" for short. 

3.2.1 非貪婪模式匹配失敗過程分析
正則表達式之 貪婪與非貪婪模式詳解(概述)
圖3-1 

構建匹配失敗的非貪婪模式的正則表達式:".*?"@ 

由于最后的“@”的存在,這個正則表達式最后一定是匹配失敗的,那么看一下匹配過程。 

首先由“"”取得控制權,由位置0處開始嘗試匹配,匹配失敗,直到圖中標示的A處匹配成功,控制權交給“.*?”。 

“.*?”取得控制權后,由A后面的位置開始嘗試匹配,由于是非貪婪模式,首先忽略匹配,將控制權交給“"”,同時記錄一下回溯狀態。“"”取得控制權后,由A后面的位置開始嘗試匹配,匹配字符“r”失敗,查找可供回溯的狀態,將控制權交給“.*?”,由“.*?”匹配字符“r”。重復以上過程,直到“.*?”匹配了B處前面的字符“n”,“"”匹配了B處的字符“””,將控制權交給“@”。由“@”匹配接下來的空格“ ”,匹配失敗,查找可供回溯的狀態,控制權交給“.*?”,由“.*?”匹配空格。繼續重復以上匹配過程,直到由“.*?”匹配到字符串結束位置,將控制權交給“"”。由于已經是字符串結束位置,匹配失敗,報告整個表達式在位置11處匹配失敗,一輪匹配嘗試結束。 

正則引擎傳動裝置使正則向前傳動,進入下一輪嘗試。后續匹配過程與第一輪嘗試匹配過程基本類似,可以參考圖3-1。 

從匹配過程中可以看到,非貪婪模式的匹配失敗過程,幾乎每一步都伴隨著回溯過程,對匹配效率的影響是很大的。 

3.2.2 貪婪模式匹配失敗過程分析——大范圍子表達式
正則表達式之 貪婪與非貪婪模式詳解(概述)

圖3-2 

PS:以上分析過程圖示參考了《精通正則表達式》一書相關章節圖示。 

構建匹配失敗的貪婪模式的正則表達式:".*"@ 

其中量詞修飾的子表達式為匹配范圍較大的“.”,由于最后的“@”的存在,這個正則表達式最后也是一定匹配失敗的,看一下匹配過程。 

首先由“"”取得控制權,由位置0處開始嘗試匹配,匹配失敗,直到圖中標示的A處匹配成功,控制權交給“.*”。 

“.*”取得控制權后,由A后面的位置開始嘗試匹配,由于是貪婪模式,優化嘗試匹配,一直匹配到字符串的結束位置,將控制權交給“"”。“"”取得控制權后,由于已經是字符串的結束位置,匹配失敗,查找可供回溯的狀態,將控制權交給“.*”,由“.*”讓出已匹配字符“.”。重復以上過程,直到后面“"”匹配了C處后面的字符“””,將控制權交給“@”。由“@”匹配接下來D處的空格“ ”,匹配失敗,查找可供回溯的狀態,控制權交給“.*”,由“.*”讓出已匹配文本。繼續重復以上匹配過程,直到由“.*”讓出所有已匹配的文本到I處,將控制權交給“"”。“"”匹配失敗,由于已經沒有可供回溯的狀態,報告整個表達式在位置11處匹配失敗,一輪匹配嘗試結束。 

正則引擎傳動裝置使正則向前傳動,進入下一輪嘗試。后續匹配過程與第一輪嘗試匹配過程基本類似,可以參考圖3-2。 

從匹配過程中可以看到,大范圍子表達式貪婪模式的匹配失敗過程,從總體上看,與非貪婪模式沒有什么區別,最終進行的回溯次數與非貪婪模式基本一致,對匹配效率的影響仍然很大。 

3.2.3 貪婪模式匹配失敗過程分析——改進的子表達式
正則表達式之 貪婪與非貪婪模式詳解(概述)
圖3-3 

構建匹配失敗的貪婪模式的正則表達式:"[^"]*"@ 

其中量詞修飾的子表達式,改為匹配范圍較小的排除型字符組“[^"]”,由于最后的“@”的存在,這個正則表達式最后也是一定匹配失敗的,看一下匹配過程。 

首先由“"”取得控制權,由位置0處開始嘗試匹配,匹配失敗,直到圖中標示的A處匹配成功,控制權交給“[^"]*”。 

“[^"]*”取得控制權后,由A后面的位置開始嘗試匹配,由于是貪婪模式,優先嘗試匹配,一直匹配到B處,將控制權交給“"”。“"”匹配接下來的的字符“"”,匹配成功,將控制權交給“@”。由“@”匹配接下來的空格“ ”,匹配失敗,查找可供回溯的狀態,控制權交給“[^"]*”,由“[^"]*”讓出已匹配文本。繼續重復以上匹配過程,直到由“[^"]*”讓出所有已匹配的文本到C處,將控制權交給“"”。“"”匹配失敗,由于已經沒有可供回溯的狀態,報告整個表達式在位置11處匹配失敗,一輪匹配嘗試結束。 

正則引擎傳動裝置使正則向前傳動,進入下一輪嘗試。后續匹配過程與第一輪嘗試匹配過程基本類似,可以參考圖3-3。 

從匹配過程中可以看到,使用了排除型字符組的貪婪模式的匹配失敗過程,從總體上看,大量減少了每輪回溯的次數,可以有效的提升匹配效率。 

3.2.4 貪婪模式匹配失敗過程分析——固化分組 
通過3.2.3節的分析可以知道,由于“[^"]*”使用了排除型字符組,那么圖3-3中,在A和B之間被匹配到的字符,就一定不會是字符“"”,所以B到C之間回溯過程就是多余的,也就是說在這之間的可供回溯的狀態完全可以不記錄。.NET中可以使用固化分組,Java中可以使用占有優先量詞來實現這一效果。 

正則表達式之 貪婪與非貪婪模式詳解(概述)
圖3-4 

首先由“"”取得控制權,由位置0處開始嘗試匹配,匹配失敗,直到圖中標示的A處匹配成功,控制權交給“(?>[^"]*)”。 

“(?>[^"]*)”取得控制權后,由A后面的位置開始嘗試匹配,由于是貪婪模式,優先嘗試匹配,一直匹配到B處,將控制權交給“"”,在這一匹配過程中,不記錄任何可供回溯的狀態。“"”匹配接下來的字符“””,匹配成功,將控制權交給“@”。由“@”匹配接下來的空格“ ”,匹配失敗,查找可供回溯的狀態,由于已經沒有可供回溯的狀態,報告整個表達式在位置11處匹配失敗,一輪匹配嘗試結束。 

正則引擎傳動裝置使正則向前傳動,進入下一輪嘗試。后續匹配過程與第一輪嘗試匹配過程基本類似,可以參考圖3-4。 

從匹配過程中可以看到,使用了固化分組的貪婪模式的匹配失敗過程,沒有涉及到回溯,可以最大限度的提升匹配效率。 

3.3 非貪婪模式向貪婪模式的轉換 
使用匹配范圍較大的子表達式時,貪婪模式與非貪婪模式匹配到的內容會有所不同,但是通過優化子表達式,非貪婪模式可以實現的匹配,貪婪模式都可以實現。 

比如在實際應用中,匹配img標簽的內容。 

舉例: 

需求:取得img標簽中的圖片地址,src=后固定為“”” 

源字符串:<img class="test" src="/img/logo.gif" height="230" src="/uploads/allimg/141125/1404353c4-7.jpg" style="border: 1px solid rgb(204, 204, 204); padding: 3px; max-width: 620px; overflow: hidden;" width="590" />

圖4-1 

首先由“<”取得控制權,由位置0位開始嘗試匹配,匹配字符“a”,匹配失敗,第一輪匹配結束。第二輪匹配從位置1開始嘗試匹配,同樣匹配失敗。第三輪從位置3開始嘗試匹配,匹配字符“<”,匹配成功,控制權交給“d”。 

“d”嘗試匹配字符“d”,匹配成功,控制權交給“i”。重復以上過程,直到由“>”匹配到字符“>”,控制權交給“.*”。 

“.*”屬于貪婪模式,將從B處后的字符“t”開始,一直匹配到E處,也就是字符串結束位置,將控制權交給“<”。

“<”從字符串結束位置嘗試匹配,匹配失敗,向前查找可供回溯的狀態,把控制權交給“.*”,由“.*”讓出一個字符“c”,把控制權再交給“<”,嘗試匹配,匹配失敗,向前查找可供回溯的狀態。一直重復以上過程,直到“.*”讓出已匹配的字符“<”,實際上也就是讓出了已匹配的子串“</div>cc”為止,“<”才匹配字符“<”成功,控制權交給“/”。 

接下來由“/”、“d”、“i”、“v”分別匹配對應的字符成功,此時整個正則表達式匹配完畢。 

4.2 貪婪與非貪婪——量詞的細節 
4.2.1 區間量詞的非貪婪模式 
前面提到的非貪婪模式,一直都是使用的“*?”,而沒有涉及到其它的區間量詞,對于“*?”和“+?”這樣的非貪婪模式,大多數接觸過正則表達式的人都可以理解,但是對于區間量詞的非貪婪模式,比如“{m,n}?”,要么是沒見過,要么是不理解,主要是這種應用場景非常少,所以被忽略了。 

首先需要明確的一點,就是量詞“{m,n}”是匹配優先量詞,雖然它有了上限,但是在達到上限之前,能夠匹配,還是要盡可能多的匹配的。而“{m,n}?”就是對應的忽略優先量詞了,在可匹配可不匹配的情況下,盡可能少的匹配。 

接下來舉一個例子說明這種非貪婪模式的應用。 

舉例(參考 限制字符長度與最小匹配): 

需求:如何限制在長度為100的字符串中,從頭匹配到最先出現的abc 

csdn.{1,100}abc 這樣寫是最大匹配(1-100個字符串中,我需要最小的) 

比如csdnfddabckjdsfjabc,匹配結果應為:csdnfddabc 

正則表達式:csdn.{1,100}?abc 

或許對這個例子還有人不是很理解,但是想想,其實“*”就等價于“{0,}”,“+”就等價于“{1,}”,“*?”也就是“{0,}?”,抽象出來也就是“{m,}?”,即上限為無窮大。如果上限為一個固定值,那就是“{m,n}?”,這樣應該也就可以理解了。 

“{m}”沒有放在匹配優先量詞中,同樣的,“{m}?”雖然被部分語言所支持,但是也沒有放在忽略優先量詞中,主要是因為這兩種量詞,實現的效果是一樣的,只有被修飾的子表達式匹配m次才能匹配成功,且沒有可供回溯的狀態,所以也不存在是匹配優先還是忽略優先的問題,也就不在本文的討論范圍內。事實上即使討論也沒有意義的,只要知道它們的匹配行為也就是了。 

4.2.2 忽略優先量詞的匹配下限 
對于匹配優先量詞的匹配下限很好理解,“?”等價于“{0,1}”,它修飾的子表達式,最少匹配0次,最多匹配1次;“*”等價于“{0,}”,它修飾的子表達式,最少匹配0次,最多匹配無窮多次;“+”等價于“{1,}”,它修飾的子表達式,最少匹配1次,最多匹配無窮多次。 

對于忽略優先量詞的下限,也是容易忽略的。 

“??”也是忽略優先量詞,被修飾的子表達式使用的也是非貪婪模式,“??”修飾的子表達式,最少匹配0次,最多匹配1次。在匹配過程中,遵循非貪婪模式匹配原則,先不匹配,即匹配0次,記錄回溯狀態,只有不得不匹配時,才去嘗試匹配。 

“*?”修飾的子表達式,最少匹配0次,最多匹配無窮多次;“+?”修飾的子表達式,最少匹配1次,最多匹配無窮多次,“+?”雖然使用的是非貪婪模式,在匹配過程中,首先要匹配一個字符,之后才是忽略匹配的,這一點也需要注意。 

4.3 貪婪與非貪婪模式小結 
Ø 從語法角度看貪婪與非貪婪 

被匹配優先量詞修飾的子表達式,使用的是貪婪模式;被忽略優先量詞修飾的子表達式,使用的是非貪婪模式。 

匹配優先量詞包括:“{m,n}”、“{m,}”、“?”、“*”和“+”。 

忽略優先量詞包括:“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。 

Ø 從應用角度看貪婪與非貪婪 

貪婪與非貪婪模式影響的是被量詞修飾的子表達式的匹配行為,貪婪模式在整個表達式匹配成功的前提下,盡可能多的匹配;而非貪婪模式在整個表達式匹配成功的前提下,盡可能少的匹配。非貪婪模式只被部分NFA引擎所支持。 

Ø 從匹配原理角度看貪婪與非貪婪 

能達到同樣匹配結果的貪婪與非貪婪模式,通常是貪婪模式的匹配效率較高。 

所有的非貪婪模式,都可以通過修改量詞修飾的子表達式,轉換為貪婪模式。 

貪婪模式可以與固化分組結合,提升匹配效率,而非貪婪模式卻不可以。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 日韩美香港a一级毛片免费 欧美一级淫片007 | xxxx18韩国护士hd老师 | 主人在调教室性调教女仆游戏 | 国产va在线观看免费 | 日韩视频1 | 欧美成人精品h版在线观看 国产一级淫片在线观看 | 久久久久免费精品国产小说色大师 | 小雪奶水翁胀公吸小说最新章节 | 国产免费专区 | 成码无人av片在线观看网站 | 蜜桃视频最新网址 | 久久99久久98精品免观看软件 | 欧美成人午夜影院 | 91精品国产综合久久婷婷香蕉 | 伊人在线视频 | 看a级毛片| 内地av在线| 好吊色欧美一区二区三区四区 | 欧美精品一区二区三区在线 | 72pao成人国产永久免费视频 | 青草久久网 | 国产寡妇xxxxxxxx性开放 | 欧美性生交xxxxx免费观看 | av免费在线观看国产 | 激情视频在线播放 | 韩国美女一区 | 毛片在线免费视频 | 成人免费观看av | 免费一级特黄毛片 | 成年人小视频在线观看 | 超碰97人人艹 | 免费观看视频91 | 欧洲成人一区二区 | 久久精品站 | 黄色大片高清 | 亚洲生活片 | 午夜视频免费播放 | 欧美a∨一区二区三区久久黄 | 欧美日韩在线免费观看 | 毛片在线免费观看视频 | 欧美日韩精品不卡一区二区三区 |