(1)關(guān)注如何讓匹配更快失敗
正則表達(dá)式處理慢往往是因為匹配失敗過程慢,而不是匹配成功過程慢。使用正則表達(dá)式匹配一個很大字符串的一小部分,情況更為嚴(yán)重,正則表達(dá)式匹配失敗的位置比匹配成功的位置要多得多。一個修改使正則表達(dá)式匹配更快但失敗更慢,例如,通過增加所需的回溯次數(shù)嘗試所有分支的排列組合,這通常是一個失敗的修改。
(2)正則表達(dá)式以簡單的、必需的字元開始
最理想的情況是,一個正則表達(dá)式的起始字元應(yīng)當(dāng)盡可能快速地測試并排除明顯不匹配的位置。用于此目的好的起始字元通常是一個錨(^或$)、特定字符(如x 或\u363A)、字符類(如[a-z]或速記符、單詞邊界(\b))。如果可能,避免以分組或選擇字元開頭,避免頂級分支,如/one|two/,因為這樣會強迫正則表達(dá)式識別多種起始字元。Firefox瀏覽器對起始字元中使用的任何量詞都很敏感,能夠優(yōu)化得更好。例如,以\s\s*替代\s+或\s{1,}。其他瀏覽器大多優(yōu)化掉這些差異。
(3)編寫量詞模板,使它們后面的字元互相排斥
當(dāng)字符與字元相鄰或子表達(dá)式能夠重疊匹配時,一個正則表達(dá)式嘗試分解文本的路徑數(shù)量將增加。為避免出現(xiàn)此現(xiàn)象,盡量具體化模板。當(dāng)表達(dá)“[^"\r\n]*”時不要使用“.*?”(依賴回溯)。
(4)減少分支的數(shù)量,縮小它們的范圍
當(dāng)分支使用 | (豎線)時,可能要求在字符串的每一個位置上測試所有的分支選項。通??赏ㄟ^使用字符類和選項組件減少對分支的需求,或者將分支在正則表達(dá)式上的位置推后(允許到達(dá)分支之前的一些匹配嘗試失?。?。
字符類比分支更快,因為它們使用位向量實現(xiàn)(或其他快速實現(xiàn))而不是回溯。當(dāng)分支必不可少時,在不影響正則表達(dá)式匹配的情況下,將常用分支放在最前面。分支選項從左向右依次嘗試,一個選項被匹配上的機會越多,它被檢測的速度就越快。
注意:由于Chrome 和Firefox 瀏覽器自動執(zhí)行這些優(yōu)化中的某些項目,因此較少受到手工調(diào)整的影響。
(5)使用非捕獲組
捕獲組花費時間和內(nèi)存用于記錄后向引用,并保持它們是最新的。如果不需要一個后向引用,可通過使用非捕獲組避免這種開銷,例如,(?:…)替代(…)。當(dāng)需要一個完全匹配的后向引用時,有些人喜歡將正則表達(dá)式包裝在一個捕獲組中,這是不必要的,因為可以通過其他方法引用完全匹配。例如,使用regex.exec()返回數(shù)組的第一個元素,或替換字符串中的$&。用非捕獲組取代捕獲組在Firefox 瀏覽器中影響很小,但在其他瀏覽器上處理長字符串時影響很大。
(6)捕獲感興趣的文字,減少后處理
如果要引用匹配的一部分,應(yīng)當(dāng)通過一切手段,捕獲那些片斷,再使用后向引用處理。例如,編寫代碼處理一個正則表達(dá)式所匹配的引號中的字符串內(nèi)容,使用/"([^"]*)"/之后再使用一次后向引用,而不是使用/"[^"]*"/之后從結(jié)果中手工剝離引號。當(dāng)在循環(huán)中使用時,減少這方面的工作可以節(jié)省大量時間。
(7)暴露所需的字元
為幫助正則表達(dá)式引擎在如何優(yōu)化查詢例程時做出明智的決策,應(yīng)盡量簡單地判斷出那些必需的字元。當(dāng)字元應(yīng)用在子表達(dá)式或分支中時,正則表達(dá)式引擎很難判斷它們是不是必需的,有些引擎并不做此方面的努力。例如,正則表達(dá)式/^(ab|cd)/暴露它的字符串起始錨。IE 和Chrome瀏覽器會注意到這一點,并阻止正則表達(dá)式嘗試查找字符串頭端之后的匹配,從而使查找瞬間完成而不管字符串長度。但是,由于等價正則表達(dá)式/(^ab|^cd)/不暴露它的^錨,IE無法應(yīng)用同樣的優(yōu)化,最終無意義地搜索字符串并在每一個位置上匹配。
(8)使用適當(dāng)?shù)牧吭~
“貪婪”量詞和“懶惰”量詞即使匹配同樣的字符串,其查找匹配過程也是不同的。在確保正確等價的前提下,使用更合適的量詞類型(基于預(yù)期的回溯次數(shù))可以顯著提高性能,尤其在處理長字符串時。