1. 程式人生 > >正則表示式優化總結

正則表示式優化總結

什麼是正在表示式

正則表示式(regular expression)描述了一種字串匹配的模式(pattern),可以用來檢查一個串是否含有某種子串、將匹配的子串替換或者從某個串中取出符合某個條件的子串等。

正則表示式快速入門可參考:https://www.w3cschool.cn/regex_rmjc/。正則表示式裡包括普通字元(例如,a 到 z 之間的字母)和特殊字元(稱為"元字元")。簡單的基礎不用多講,講幾個正則中重要又稍微難懂的概念。

 

1、回溯

正則引擎主要可以分為基本不同的兩大類:一種是DFA(確定性有窮自動機),另一種是NFA(非確定性有窮自動機)。在NFA中由於表示式主導的序列匹配方式,所以用到了回溯(backtracking),這個是NFA最重要的部分,每一次某個分支的匹配失敗都會導-致一次回溯。

回溯法也稱試探法,它的基本思想是:從問題的某一種狀態(初始狀態)出發,搜尋從這種狀態出發所能達到的所有“狀態”,當一條路走到“盡頭”的時候(不能再前進),再後退一步或若干步,從另一種可能“狀態”出發,繼續搜尋,直到所有的“路徑”(狀態)都試探過。這種不斷“前進”、不斷“回溯”尋找解的方法,就稱作“回溯法”。

舉個例子更能直觀說明。正則是 /ab{1,3}c/ ,其視覺化形式是:

而當目標字串是"abbbc"時,就沒有所謂的“回溯”。其匹配過程是:

 

 

如果目標字串是"abbc",中間就有回溯。

 

2、貪婪匹配和非貪婪匹配

當正則表示式中包含能接受重複的限定符時,通常的行為是(在使整個表示式能得到匹配的前提下)匹配儘可能多的字元

。以這個表示式為例: a.*b ,它將會匹配最長的以a開始,以b結束的字串。如果用它來搜尋 aabab 的話,它會匹配整個字串 aabab。這被稱為貪婪匹配。

非貪婪匹配就是儘可能少的匹配。一下幾個是非貪婪匹配常見的用法。

以 a.*?b 為例,用它來搜尋 aabab 的話,他它會匹配 aab(第一到第三個字元)和 ab(第四到第五個字元)

3、捕獲和斷言

捕獲:當我們使用小括號指定一個子表示式之後,就要對這個子表示式的文字進行匹配,即此分組捕獲的內容,可以在表示式或其它程式中作進一步的處理。一般情況下,每個分組都會自動擁有一個組號,它的規則是:從左到右以分組的左括號作為標誌,把第一次出現的分組的組號定為1,第二個即2,以此類推下去。

後向引用:用於重複搜尋前面某個分組匹配的文字。例如,\1代表分組1匹配的文字。我們根據示例來深刻理解: \b(\w+)\b\s+\1\b 可以用來匹配重複的單詞,像go go。

非捕獲組:第三個 (?:exp) 不會改變正則表示式的處理方式,只是這樣的組匹配的內容不會像前兩種那樣被捕獲到某個組裡面,也不會擁有組號。

零寬斷言:用於查詢在某些內容的之前或之後的東西,但是有不包含這些內容本身的時候,零寬斷言就起到作用了。

(?=exp) :也叫零寬度正預測先行斷言,它斷言自身出現的位置的後面能匹配表示式exp。比如 \b\w+(?=ing\b),匹配以ing結尾的單詞的前面部分(除了ing以外的部分),如查詢I'm singing while you're dancing.時,它會匹配sing和danc。

(?<=exp) :也叫零寬度正回顧後發斷言,它斷言自身出現的位置的前面能匹配表示式exp。比如 (?<=\bre)\w+\b 會匹配以re開頭的單詞的後半部分(除了re以外的部分),例如在查詢reading a book時,它匹配ading。

 

正則優化

正則在安全領域中最常見的用途是用來編寫安全策略,比如WAF的攔截策略以及HIDS對應的webshell檢測策略等。以下歸納了幾點正則優化的策略:

a)  合理使用括號

當要捕獲組的時候,使用非捕獲型括號(?:),這是寫策略正則最常用的優化方法,因為使用(?:)可以匹配想要的內容,但不捕獲到組裡,可以節省資源,提高效率。

b)  使用非貪婪模式

儘量使用非貪婪模式,因為貪婪模式情況下,容易造成回溯。如果不確定使用哪種模式,優先考慮

c)使用字元組代替分支條件

使用[a-d]表示a~d之間的字母,而不是使用(a|b|c|d)

d)  謹慎用點號元字元,儘可能不用星號和加號這樣的任意量詞

例子: 要匹配 <12345>,其中<>中間是1-5位的數字

正常寫法: <\d*>

優化寫法: <\d{1,5}>

e)提取多選結構開頭的相同字元

例如 the|this 改成th(?:e|is)

f)使用佔有優先量詞和固化分組

佔有優先量詞:

?+ *+ ++ {m,n}+

佔有優先量詞與匹配優先量詞很相似,只是它們從來不會交還已經匹配的字元。

固化分組:

(?>...)     ...是指具體內容

固化分組的內容與正常的匹配並無區別,只是當匹配完括號中的內容後,括號中的備用狀態會全部捨去。

g)始、行描點優化

能確定起止位置,使用^能提高匹配的速度。同理,使用$標記結尾,正則引擎則會從符合條件的長度處開始匹配,略過目標字串中許多可能的字元。在寫正則表示式時,應該將描點獨立出來,例如“^(?:abc|123)”比“^123|^abc”效率高,而“^(abc)”比“(^abc)”效率更高。