正則表達式原理和優化

01. 正則匹配原理

最近工做中用到了不少正則匹配。才學習了正則表達式匹配原理以及優化。正則表達式

1. 解析引擎眼中的字符串組成

對於字符串「DEF」而言,包括D、E、F三個字符和 0、一、二、3 四個數字位置:0D1E2F3,對於正則表達式而言全部源字符串,都有字符和位置。正則表達式會從0號位置,逐個去匹配的。安全

2. 佔有字符和零寬度

正則表達式匹配過程當中,若是子表達式匹配到的是字符內容,而非位置,並被保存到最終的匹配結果中,那麼就認爲這個子表達式是佔有字符的;若是子表達式匹配的僅僅是位置,或者匹配的內容並不保存到最終的匹配結果中,那麼就認爲這個子表達式是零寬度的。佔有字符是互斥的,零寬度是非互斥的。也就是一個字符,同一時間只能由一個子表達式匹配,而一個位置,卻能夠同時由多個零寬度的子表達式匹配。常見零寬字符有:^,(?=)等性能優化

3. 正則表達式匹配過程詳解實例

咱們掌握了上面幾個概念,咱們接下來分析下幾個常見的解析過程。結合使用軟件regexBuddy來分析。併發

Demo1:

源字符DEF,對應標記是:0D1E2F3,匹配正則表達式是:/DEF/

過程能夠理解爲:首先由正則表達式字符 /D/ 取得控制權,從位置0開始匹配,由 /D/ 來匹配「D」,匹配成功,控制權交給字符 /E/ ;因爲「D」已被 /D/ 匹配,因此 /E/ 從位置1開始嘗試匹配,由 /E/ 來匹配「E」,匹配成功,控制權交給 /F/ ;由 /F/ 來匹配「F」,匹配成功。工具

Demo2:

源字符DEF,對應標記是:0D1E2F3,匹配正則表達式是:/D\w+F/

過程能夠理解爲:首先由正則表達式字符 /D/ 取得控制權,從位置0開始匹配,由 /D/ 來匹配「D」,匹配成功,控制權交給字符 /\w+/ ;因爲「D」已被 /D/ 匹配,因此 /\w+/ 從位置1開始嘗試匹配,\w+貪婪模式,會記錄一個備選狀態,默認會匹配最長字符,直接匹配到EF,而且匹配成功,當前位置3了。而且把控制權交給 /F/ ;由 /F/ 匹配失敗,\w+匹配會回溯一位,當前位置變成2。並把控制權交個/F/,由/F/匹配字符F成功。所以\w+這裏匹配E字符,匹配完成!性能

參考《http://www.jb51.net/article/73403.htm》學習

02. 正則表達式性能優化方法

其實主要是它的「回溯」,減小「回溯」次數(減小循環查找同一個字符次數),是提升性能的主要方法。優化

思路:.net

  1. 使用正確的邊界匹配器(^、$、\b、\B等),限定搜索字符串位置
  2. 儘可能不適用通配符".";字符使用具體的元字符、字符類(\d、\w、\s等)(推薦)
  3. 使用正確的量詞(+、*、?、{n,m}),若是可以限定長度,匹配最佳
  4. 使用非捕獲組、原子組,減小沒有必要的字匹配捕獲用(?:),減小內存。

附上正則表達式基礎:
《https://deerchao.net/tutorials/regex/regex.htm#lookaround》線程

還有查看分析的軟件:RegexBuddy

匹配實例
《http://www.jb51.net/article/85895.htm》
《http://blog.csdn.net/king_xing/article/details/52771465》

03. 代碼優化

Java中通常是使用Patter和Matcher進行正則匹配。
以前寫的方法:

/**
 * 匹配str中的正則結果
 *
 * @param regEx 正則式String
 * @param str   待匹配的源字符串
 * @return 返回匹配的捕獲組1
 */
public static String regExMatcher(String regEx, String str) {
    Pattern pattern = Pattern.compile(regEx);
    Matcher matcher = pattern.matcher(str);
    return matcher.find() ? matcher.group(1) : "";
}

上面的優化都是針對正則表達式自己的,可是其實正則的損耗還來源於Pattern.compile編譯的過程。在這個過程當中創建語法分析樹。

所以,對於屢次執行的相同pattern的匹配工具類,應該將Pattern.compile(regEx);抽取成成員變量,在類中初始化。

Ps: Pattern和String同樣,是不可變類,即編譯後該對象的內容就肯定了,沒法修改,只讀的,Pattern必然是線程安全的,併發使用沒有安全隱患;
在matcher()方法中也並不能對其修改

所以,改成:

/**
 * 匹配str中的正則結果
 *
 * @param regExPattern 已編譯好的Pattern模式
 * @param str          待匹配的源字符串
 * @return 返回匹配的捕獲組1
 */
public static String regExMatcher(Pattern regExPattern, String str) {
    Matcher matcher = regExPattern.matcher(str);
    return matcher.find() ? matcher.group(1) : "";
}
相關文章
相關標籤/搜索