正則的回溯
在正則表達式實現中,回溯是匹配過程的基本組成部分,它是正則表達式如此好用和強大的根源。然而,回溯計算代價很高,如果設計失誤,將導致失控。回溯是影響整體性能的唯一因素,理解它的工作原理,以及如何減小使用頻率,可能是編寫高效正則表達式的關鍵點。
當一個正則表達式掃描目標字符串時,從左到右逐個掃描正則表達式的組成部分,在每個位置上測試能不能找到一個匹配。對於每一個量詞和分支,都必須確定如何繼續進行。如果是量詞(如*+?或者{2,}),那么正則表達式必須確定何時嘗試匹配更多的字符;如果遇到分支(通過|操作符),那么正則表達式必須從這些選項中選擇一個進行嘗試。
當正則表達式做出這樣的決定時,如果有必要,它會記住另一個選項,以備返回后使用。如果所選方案匹配成功,正則表達式將繼續掃描正則表達式模板,如果其余部分匹配也成功了,那么匹配就結束了。
但是,如果所選擇的方案未能發現相應匹配,或者后來的匹配也失敗了,正則表達式將回溯到最后一個決策點,然后在剩余的選項中選擇一個。繼續這樣,直到找到一個匹配,或者量詞和分支選項的所有可能的排列組合都嘗試失敗后放棄這一過程,然后移動到此過程開始位置的下一個字符上,重復此過程。
例如:abc123d
當正則引擎用正則w*(d+)去匹配字符串abc123d時,會先用w*去匹配字符串abc123d,
首先,w*會匹配字符串abc123d的所有字符,
然后再交給d+去匹配剩下的字符串,而剩下的沒了,這時,w*規則會吐出一個字符,給d+去匹配,同時,在吐出字符之前,記錄一個點,這個點,就是用於回溯的點;
然后d+去匹配n,發現並不能匹配成功,w*再吐出一個字符,w*會先再次記錄一個回溯的點,再吐出一個字符。
這時,w* 匹配的結果只有abc12了,已經吐出3d了,d+再去匹配3,發現匹配成功,則會通知引擎,匹配成功了,就直接顯示出來了。
所以,(d+)的結果是3,而不是123。
正則表達式中匹配的三種量詞:貪婪(Greedy)、勉強(Reluctant)、獨占(Possessive)
貪婪 非貪婪(?) 獨占(+)
X? X?? X?+
X* X*? X*+
X+ X+? X++
X{n} X{n}? X{n}+
X{n,} X{n,}? X{n,}+
X{n,m} X{n,m}? X{n,m}+
Greedy:貪婪
匹配最長。在貪婪量詞模式下,正則表達式會盡可能長地去匹配符合規則的字符串,且會回溯。
Reluctant :非貪婪
匹配最短。在非貪婪量詞模式下,正則表達式會匹配盡可能短的字符串。
Possessive :獨占
同貪婪一樣匹配最長。不過在獨占量詞模式下,正則表達式盡可能長地去匹配字符串,一旦匹配不成功就會結束匹配而不會回溯。