例子:假設在fred and barney went bowling last night 上使用/fred.+barney/進行匹配。我們知道Regex
將匹配上,下面我們具體的講解這一個過程:首先,子模式fred 將匹配其對應的字串。模式的下一部分是.+,它將匹配除了分行符號之外的任一字元,次數大於等於一。但,由於加號(+)是貪婪的;它將儘可能的進行匹配。因此,它將匹配剩餘的所有字串,包括night。
現在對banrey 進行匹配,但不能成功,因為已經到了字串的結尾處。由於.+在少一個字元的情況下仍能匹配成功,因此它退回字串最後一個字母t。(它雖是貪婪的,但更希望整個模式能匹配成功。)子模式barney 又嘗試匹配,結果仍是不行。因此.+再退回字母h,又進行匹配。一個字元接一個字元,.+退回其匹配的字元,直到其退回了字串barney。最後,子模式banrey 被匹配上了,現在整個模式都匹配上了。
如本例所顯示的那樣,這些操作引起了大量的後援動作,因為這些數量詞匹配了太多的字串。
因此對於每一個貪婪的數量詞,需要一種非貪婪的方法。不是使用加號(+),而是使用非貪婪的數量詞+?,它將匹配一次或多次(加號的意思),但其匹配儘可能少的次數,而非儘可能多的次數。現在我們來看看模式為/fred.+?barney/時的過程:
首先,fred 將被匹配上。接著,模式的下一部分是.+?,它匹配的字元個數不大於1,因此匹配fred 後面的空格。下一個子模式是banrey,它在這裡不能被匹配(因為現在的位置是and barney… 的開頭)。.+?再匹配a,剩下的模式繼續進行匹配。又一次,barney 不能匹配上,因此.+?再匹配n,依次類推。當.+?匹配了這5 個字元後,barney 可以被匹配上了,現在模式比對成功。
這裡也存在一些後援動作,但由於引擎只需回退,並只嘗試幾次,其在速度上會有很大提高。但,這種提高依賴於banrey在fred 的附近能被找到。如果資料中fred 在字串的開頭,而barney 在結尾處,則貪婪數量詞方法的速度更快。因此,Regex的速度依賴於具體的資料。
非貪婪數量詞不僅和效率相關。即便它和其對應的貪婪數量詞運算式均能匹配(或者不能匹配)同一個字串,它們匹配的部分也可能是不同的。例如,假設你有一些HTML 類型的文本,你想移除標記<BOLD>和</BOLD>,而保留其間的內容。下面是文本:
I’am talking about the cartoon with Fred and <BOLD>Wilma</BOLD>!
下面是一種移除標記的方法。它有什麼錯誤呢?
s#<BOLD>(.*)</BOLD>#$1#g;
其問題出在星號是貪婪的。如果文本變成了下面的樣子,會得到什麼結果?
I thought you said Fred and <BOLD>Velma</BOLD>, not <BOLD>Wilma</BOLD>
此時,模式將匹配從第一個<BOLD>到最後一個</BOLD>之間的內容,中間的部分被保留下來。噢!我們需要非貪婪的數量詞。星號的非貪婪的類型是*?,因此此模式應當是:
$#<BOLD>(.*?)</BOLD>#$1#g;
現在它能正確執行了。
由於加號的非貪婪類型是+?,星號的為*?,你可能已經意識到剩下的兩種數量詞其對應的類型也是類似的。花括弧的非貪婪類型看起來一樣,只是在閉花括弧後有一個問號,如{5,10}?或者{8,}?。甚至問號數量詞也有非貪婪類型:??。它匹配一次或者0 次,但傾向於匹配0 次。