讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議46:提高正則表達式執行效率 >

建議46:提高正則表達式執行效率

(1)關注如何讓匹配更快失敗

正則表達式處理慢往往是因為匹配失敗過程慢,而不是匹配成功過程慢。使用正則表達式匹配一個很大字符串的一小部分,情況更為嚴重,正則表達式匹配失敗的位置比匹配成功的位置要多得多。一個修改使正則表達式匹配更快但失敗更慢,例如,通過增加所需的回溯次數嘗試所有分支的排列組合,這通常是一個失敗的修改。

(2)正則表達式以簡單的、必需的字元開始

最理想的情況是,一個正則表達式的起始字元應當盡可能快速地測試並排除明顯不匹配的位置。用於此目的好的起始字元通常是一個錨(^或$)、特定字符(如x或u363A)、字符類(如[a-z]或速記符、單詞邊界(b))。如果可能,避免以分組或選擇字元開頭,避免頂級分支,如/one|two/,因為這樣會強迫正則表達式識別多種起始字元。Firefox瀏覽器對起始字元中使用的任何量詞都很敏感,能夠優化得更好。例如,以ss*替代s+或s{1,}。其他瀏覽器大多優化掉這些差異。

(3)編寫量詞模板,使它們後面的字元互相排斥

當字符與字元相鄰或子表達式能夠重疊匹配時,一個正則表達式嘗試分解文本的路徑數量將增加。為避免出現此現象,盡量具體化模板。當表達「[^\"rn]*」時不要使用「.*?」(依賴回溯)。

(4)減少分支的數量,縮小它們的範圍

當分支使用|(豎線)時,可能要求在字符串的每一個位置上測試所有的分支選項。通常可通過使用字符類和選項組件減少對分支的需求,或者將分支在正則表達式上的位置推後(允許到達分支之前的一些匹配嘗試失敗)。

字符類比分支更快,因為它們使用位向量實現(或其他快速實現)而不是回溯。當分支必不可少時,在不影響正則表達式匹配的情況下,將常用分支放在最前面。分支選項從左向右依次嘗試,一個選項被匹配上的機會越多,它被檢測的速度就越快。

注意:由於Chrome和Firefox瀏覽器自動執行這些優化中的某些項目,因此較少受到手工調整的影響。

(5)使用非捕獲組

捕獲組花費時間和內存用於記錄後向引用,並保持它們是最新的。如果不需要一個後向引用,可通過使用非捕獲組避免這種開銷,例如,(?:…)替代(…)。當需要一個完全匹配的後向引用時,有些人喜歡將正則表達式包裝在一個捕獲組中,這是不必要的,因為可以通過其他方法引用完全匹配。例如,使用regex.exec返回數組的第一個元素,或替換字符串中的$&。用非捕獲組取代捕獲組在Firefox瀏覽器中影響很小,但在其他瀏覽器上處理長字符串時影響很大。

(6)捕獲感興趣的文字,減少後處理

如果要引用匹配的一部分,應當通過一切手段,捕獲那些片斷,再使用後向引用處理。例如,編寫代碼處理一個正則表達式所匹配的引號中的字符串內容,使用/\"([^\"]*)\"/之後再使用一次後向引用,而不是使用/\"[^\"]*\"/之後從結果中手工剝離引號。當在循環中使用時,減少這方面的工作可以節省大量時間。

(7)暴露所需的字元

為幫助正則表達式引擎在如何優化查詢例程時做出明智的決策,應盡量簡單地判斷出那些必需的字元。當字元應用在子表達式或分支中時,正則表達式引擎很難判斷它們是不是必需的,有些引擎並不做此方面的努力。例如,正則表達式/^(ab|cd)/暴露它的字符串起始錨。IE和Chrome瀏覽器會注意到這一點,並阻止正則表達式嘗試查找字符串頭端之後的匹配,從而使查找瞬間完成而不管字符串長度。但是,由於等價正則表達式/(^ab|^cd)/不暴露它的^錨,IE無法應用同樣的優化,最終無意義地搜索字符串並在每一個位置上匹配。

(8)使用適當的量詞

正如建議45所討論過的那樣,「貪婪」量詞和「懶惰」量詞即使匹配同樣的字符串,其查找匹配過程也是不同的。在確保正確等價的前提下,使用更合適的量詞類型(基於預期的回溯次數)可以顯著提高性能,尤其在處理長字符串時。

(9)將正則表達式賦給變量,以重用它們

將正則表達式賦給變量以避免對它們重新編譯。有人使用正則表達式緩存池,以避免對給定的模板和標記組合進行多次編譯。不要過分擔心,正則表達式編譯得很快。重要的是避免在循環體中重複編譯正則表達式。換句話說,不要這樣做:


while(/regex1/.test(str1)){

/regex2/.exec(str2);

...

}


替代做法如下:


var regex1=/regex1/,

regex2=/regex2/;

while(regex1.test(str1)){

regex2.exec(str2);

...

}


(10)將複雜的正則表達式拆分為簡單的片斷

盡量避免一個正則表達式做太多的工作。處理複雜的搜索問題需要將條件邏輯拆分為兩個或多個正則表達式,這樣更容易解決問題,通常也更高效,每個正則表達式只在最後的匹配結果中執行查找。在一個模板中完成所有工作的正則表達式很難維護,而且容易引起回溯相關的問題。