01. 正則匹配原理
最近工作中用到了很多正則匹配。才學習了正則表達式匹配原理以及優化。
1. 解析引擎眼中的字符串組成
對于字符串“DEF”而言,包括D、E、F三個字符和 0、1、2、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. 正則表達式性能優化方法
其實主要是它的“回溯”,減少“回溯”次數(減少循環查找同一個字符次數),是提高性能的主要方法。
思路:
- 使用正確的邊界匹配器(^、$、\b、\B等),限定搜索字符串位置
- 盡量不適用通配符".";字符使用具體的元字符、字符類(\d、\w、\s等)(推薦)
- 使用正確的量詞(+、*、?、{n,m}),如果能夠限定長度,匹配最佳
- 使用非捕獲組、原子組,減少沒有必要的字匹配捕獲用(?:),減少內存。
附上正則表達式基礎:
《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) : "";
}
文章列表