最近工做中用到了不少正則匹配。才學習了正則表達式匹配原理以及優化。正則表達式
對於字符串「DEF」而言,包括D、E、F三個字符和 0、一、二、3 四個數字位置:0D1E2F3,對於正則表達式而言全部源字符串,都有字符和位置。正則表達式會從0號位置,逐個去匹配的。安全
正則表達式匹配過程當中,若是子表達式匹配到的是字符內容,而非位置,並被保存到最終的匹配結果中,那麼就認爲這個子表達式是佔有字符的;若是子表達式匹配的僅僅是位置,或者匹配的內容並不保存到最終的匹配結果中,那麼就認爲這個子表達式是零寬度的。佔有字符是互斥的,零寬度是非互斥的。也就是一個字符,同一時間只能由一個子表達式匹配,而一個位置,卻能夠同時由多個零寬度的子表達式匹配。常見零寬字符有:^,(?=)等性能優化
咱們掌握了上面幾個概念,咱們接下來分析下幾個常見的解析過程。結合使用軟件regexBuddy來分析。併發
源字符DEF,對應標記是:0D1E2F3,匹配正則表達式是:/DEF/
過程能夠理解爲:首先由正則表達式字符 /D/ 取得控制權,從位置0開始匹配,由 /D/ 來匹配「D」,匹配成功,控制權交給字符 /E/ ;因爲「D」已被 /D/ 匹配,因此 /E/ 從位置1開始嘗試匹配,由 /E/ 來匹配「E」,匹配成功,控制權交給 /F/ ;由 /F/ 來匹配「F」,匹配成功。工具
源字符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》學習
其實主要是它的「回溯」,減小「回溯」次數(減小循環查找同一個字符次數),是提升性能的主要方法。優化
思路:.net
附上正則表達式基礎:
《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》
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) : ""; }