正規表示式中的量詞

2021-09-21 06:04:05 字數 3283 閱讀 2633

自我感覺量詞是正規表示式裡最不容易理解的地方,所以特別為它做了個總結。

為了容易理解,會簡單地結合正規表示式引擎的工作方式來講。正規表示式引擎分為文字導向型(text-directed engines)和正規表示式導向型(regex-directed engines)兩種。因為基本上採用的是正規表示式導向型的引擎,所以下文關於引擎工作方式的部分都是基於正規表示式導向型引擎的。

一、沒有量詞時正規表示式引擎的工作方式

在沒有量詞之前,正規表示式的乙個符號塊只能匹配文字中的乙個符號,如[abc]匹配字元a或b或c。此時,正規表示式的匹配流程非常的簡單。

正規表示式引擎按從左到右的順序讀取正規表示式中的字元塊和文字中的字元,並檢查字元塊和字元是否匹配。根據匹配的結果和匹配符號的位置,後續的操作分為四種。

匹配成功,且匹配的是正規表示式的第乙個符號塊。說明文字中以該字元開始的一段字串可能會是我們需要的字串,所以引擎接著向右讀取正規表示式中的字元塊和文字中的字元進行匹配。為了說明的方便,我們把這個字元記為a。

匹配成功,且匹配的是正規表示式的最後乙個符號塊。說明文字中從a開始到目前讀取位置的這一段字元是我們需要的字串。於是,引擎將這段文字輸出,然後接著尋找下乙個匹配的字串,它繼續向右讀取文字中的字元,但是從頭開始讀取正規表示式中的字元塊,將它們進行匹配。

匹配成功,且匹配的是正規表示式中間的符號塊。說明文字中從a開始到目前為止的這一段字元還是匹配的,如果之後的字元也匹配的話就找到所需的字串。所以引擎接著向右讀取正規表示式中的字元塊和文字中的字元進行匹配。

匹配失敗,無論匹配的是正規表示式中的哪個符號塊。說明在從文字中從a開始的各種字串中,並不存在我們所需的字串。因此,引擎重新從a的後乙個位置開始讀取字元,並從頭開始讀取正規表示式中的字元塊,進行匹配。

引擎不斷重複這樣的操作,直到讀取到文字的終結符。

比如,我們用正規表示式<[ou]l>去匹配文字this is ol。引擎先讀取正規表示式的第乙個字元塊《和文字的第乙個字元<。因為它們成功匹配,所以引擎繼續讀取正規表示式的第二個字元塊[ou]和文字的第二個字元o,也成功匹配了,就繼續……,直到匹配到》,正規表示式和字串完全匹配了,於是找到了第乙個我們所需字串。之後,引擎繼續讀取文字中的字元t和正規表示式中的第乙個字元塊<,匹配失敗,引擎讀取文字中的下乙個字元h,還是失敗,直到讀取到第15個字元<,匹配成功。然後引擎讀取正規表示式中的[ou]字元塊和文字中的/字元塊,匹配失敗,引擎重新從文字的第15個《之後開始讀取字元,從正規表示式的開頭讀取字元塊……直到引擎讀到了終結符,查詢結束,找到了乙個字串,開始於文字的第1位,結束於文字的第4位。

注意:在這種實現方式下,正規表示式會優先匹配文字最左邊的字串,而且匹配過的內容不會重複出現。如用正規表示式a.去匹配my dear grandparent,得到的匹配結果是ar gr和andpa。其中沒有arent,因為arent中的首字母a已經在andpa中被匹配了。這消除了匹配順序導致的歧義和是否能重複匹配導致的歧義。

二、量詞帶來的不確定性

但是,引入了量詞之後,事情就變得複雜了起來。量詞可以讓被修飾的字元重複若干次,如a*表示任意個a組成的字串。量詞在正規表示式中起著很大的作用,但使用中總是出現意想不到的結果。

問題的起因是,被修飾字元的重複次數往往是不確定的。文字中以同乙個字元開頭,可能會有好多種長度不同的字串形式與正規表示式匹配,這就引起了歧義。如用<.*>去匹配first

yeah,以第乙個《打頭,可以有兩種長度不同的匹配,和first

,到底採用哪一種匹配呢?總不能靠運氣吧。於是,在量詞的基礎上又給他分了三類。

量詞有三類,貪婪型量詞(greedy quantifiers),勉強型量詞(reluctant quantifiers)和占有型量詞(possessive quantifiers)。

三、貪婪型量詞和勉強型量詞

由於引擎按從左往右的順序讀取,它並不能提前預知後面的字串是什麼,也就不知道到底讓被修飾字元重複多少次能獲得乙個匹配的字串。雖然你一眼看去能看出來,上乙個例子中的.*讓.重複1次匹配文字中的p和重複10次匹配文字中的p>first有些同學可能對最大重複次數有些迷茫。它就是不考慮後面的符號塊能不能匹配的情況下,被修飾字元塊能連續匹配多少個文字中的字元。以上乙個例子來說,當引擎將正規表示式的《和文字的第乙個字元《匹配起來後,讀取到正規表示式的.*後,不考慮它之後的》,.*可以匹配文字中的p>first

yeah,這就是在嘗試匹配以文字中第乙個字元《開頭的字串時,它的最大重複次數。

從最大重複次數開始逐步減小重複次數的量詞是貪婪型量詞,而從最小重複次數開始逐步增大重複次數的量詞是勉強型量詞。

量詞預設是貪婪的,貪婪型量詞會使被修飾字元重複盡可能多的次數。用以上例子來說明引擎處理貪婪型量詞的方式,首先引擎讀取了正規表示式的首字元塊《和文字的首字元<,並且匹配成功。之後引擎會讀取正規表示式的.*,.可以匹配所有的字元,而*使.重複出現,而*又是貪婪的,所以引擎會不停地重複用.去匹配文字中的字元,直到讀到文字的終結符,.和終結符匹配失敗,這個時候.的重複次數達到最大了,而引擎讀取了之後的》,發現已經匹配不了了,這個時候引擎知道重複最大次數是不行的,於是讓.少重複了一次,.*少匹配了文字中最後的h,引擎拿著這個h和正規表示式的》去匹配,匹配還是不成功,所以這個重複此時還是不行,再減一……直到重複次數為10為止,.*吐出來的》和正規表示式的》成功匹配,而此時正規表示式中的字元塊被全部匹配了,結果就產生了。

在貪婪型量詞的後面加乙個?就成了勉強型量詞,勉強型量詞會使被修飾字元重複盡可能少的次數。對以上例子來說,正規表示式為<.*?>,匹配方式如下。首先引擎讀取了正規表示式的首字元塊《和文字的首字元<,並且匹配成功。之後引擎讀取正規表示式的.*?,*?是勉強的,引擎首先會讓.重複最小的次數,由於*代表任意次,所以最小次數為0。之後,引擎會讀取.*?之後的》,嘗試著讓它去匹配文字中的下乙個字元p,匹配失敗,這個時候引擎知道重複最小次數是匹配不了了,於是讓.多重複了一次,讓.*?匹配了p,之後再用》去匹配文字的下乙個字元》,匹配成功,此時此時正規表示式中的字元塊被全部匹配了,結果就產生了。

四、占有型量詞

在貪婪型量詞的後面加乙個+就成了占有型量詞,占有型量詞讓被修飾字元重複最大次數。乍一看和貪婪型量詞沒啥區別啊,其實少了三個字,盡可能。還是用上面的例子來說,此時正規表示式為.*+,引擎只會嘗試一種可能,前面的過程和貪婪型相同。當引擎發現.的重複次數達到最大了,引擎讀取了之後的》,發現匹配不了,這時引擎知道重複最大次數是不行的,然後整個匹配就失敗了,引擎不會去嘗試其他的重複次數。

占有型量詞可以提高匹配的效率,因為它不會遍歷所有可能性去匹配一段字串,它只匹配重複次數最大的那種可能,行就行,不行就不行。如果我們需要的結果只有在最大重複次數時才會出現,那其餘的嘗試都是不必要的

比如,我們要得到文字one

two中每對尖括號包裹的內容,那我們可能會用<.*?>,但我們也可以用表示式<[^>]*+>。後者效率更高,因為它不用嘗試其他的可能性。

正規表示式量詞

量詞什麼是量詞 出現的次數 至少出現n次,最多出現m次 例子 查詢qq號 常用量詞 至少n次 任意次 零次或一次 一次或任意次 正好n次 var oinput document.getelementsbytagname input 1 9 qq號第一位為1 9之間的數字 在正則的最開始位置,就代表起...

正規表示式的量詞

量詞,顧名思義,計算數量的詞,三個人,三,就是量詞。那麼,先把正規表示式的量詞供出來 共有 表示原子恰好出現n次。表示原子最少出現n次。最少出現n次,最多出現m次。再來供一段奇葩的亂碼 韓寒三重門1s3 郭敬明小時代1fdss 韓寒後會無期郭敬明小時代2sdf sbusbffsb 方方韓韓 方方方韓...

正規表示式的量詞

貪心 懶惰和占有量詞自身是貪心的。貪心的量詞會首先匹配整個字串。嘗試匹配時,他會選定盡可能多的內容,也就是整個 輸入。量詞首次嘗試匹配整個字串,如果失敗則回退乙個字元後再次嘗試。這個過程叫回溯。他會每次回退乙個字元,直到找到匹配的內容或者沒有字元可嘗試為止。此外,他還記錄所有的行為,因此相較另兩種方...