對於要重複單個字元,非常簡單,直接在字元後賣弄加上限定符即可,例如 a+ 表示匹配1個或一個以上的a,a?表示匹配0個或1個a。這些限定符如下所示: X?X,一次或一次也沒有X*X,零次或多次X+X,一次或多次X{n}X,恰好
n 次X{n,}X,至少 n 次X{n,m}
X,至少 n 次,但是不超過 m 次
但是我們如果要對多個字元進行重複怎麼辦呢?此時我們就要用到分組,我們可以使用小括弧"()"來指定要重複的子運算式,然後對這個子運算式進行重複,例如:(abc)? 表示0個或1個abc 這裡一個括弧的運算式就表示一個分組。
分組可以分為兩種形式,擷取的群組和非擷取的群組。
擷取的群組
擷取的群組可以通過從左至右計算其開括弧來編號。例如,在運算式 ((A)(B(C))) 中,存在四個這樣的組:
1 ((A)(B(C)))2 \A3 (B(C))4 (C)
組零始終代表整個運算式
之所以這樣命名擷取的群組是因為在匹配中,儲存了與這些組匹配的輸入序列的每個子序列。捕獲的子序列稍後可以通過 Back 引用在運算式中使用,也可以在匹配操作完成後從匹配器檢索。
Back 引用是說在後面的運算式中我們可以使用組的編號來引用前面的運算式所捕獲到的文本序列(是文本不是正則)。
例如 ([" ']).* \1 其中使用了分組,\1就是對引號這個分組的引用,它匹配包含在兩個引號或者兩個單引號中的所有字串,如,"abc" 或 " ' " 或 ' " ' ,但是請注意,它並不會對" a'或者 'a"匹配。原因上面已經說明,Back引用只是引用文本而不是運算式。
非擷取的群組
以 (?) 開頭的組是純的非捕獲 組,它不捕獲文本,也不針對組合計進行計數。就是說,如果小括弧中以?號開頭,那麼這個分組就不會捕獲文本,當然也不會有組的編號,因此也不存在Back 引用。
在Java中,支援的非擷取的群組,有如下幾種:
(?=X)X,通過零寬度的正 lookahead(?!X)X,通過零寬度的負 lookahead(?<=X)X,通過零寬度的正 lookbehind(?<!X)X,通過零寬度的負 lookbehind
這四個非擷取的群組用於匹配運算式X,但是不包含運算式的文本。
(?=X )零寬度正先行斷言。僅當子運算式 X 在此位置的右側匹配時才繼續匹配。例如,\w+(?=\d) 與後跟數位單詞匹配,而不與該數字匹配。此構造不會回溯。(?!X)零寬度負先行斷言。僅當子運算式 X 不在此位置的右側匹配時才繼續匹配。例如,例如,\w+(?!\d) 與後不跟數位單詞匹配,而不與該數字匹配。(?<=X)零寬度正後發斷言。僅當子運算式 X 在此位置的左側匹配時才繼續匹配。例如,(?<=19)99 與跟在 19 後面的 99 的執行個體匹配。此構造不會回溯。(?<!X)零寬度負後發斷言。僅當子運算式
X 不在此位置的左側匹配時才繼續匹配。例如,(?<!19)99 與不跟在 19 後面的 99 的執行個體匹配
舉例:
上面都是理論性的介紹,這裡就使用一些例子來說明一下問題:
1、測試匹配性 (?<!4)56(?=9) 這裡的含義就是匹配後面的文本56前面不能是4,後面必須是9組成。因此,可以匹配如下文本 5569 ,與4569不匹配。
2 、提取字串 提取 da12bka3434bdca4343bdca234bm 提取包含在字元a和b之間的數字,但是這個a之前的字元不能是c,b後面的字元必須是d才能提取。
例如這裡就只有3434這個數字滿足要求。那麼我們怎麼提取呢?
首先我們寫出提取這個字串的運算式: (?<!c)a(\d+)bd 這裡就只有一個擷取的群組(\d+)
JAVA程式碼片段如下:
- Pattern p = Pattern.compile("(?<!c)a(");
- Matcher m = p.matcher("da12bca3434bdca4343bdca234bm");
- while(m.find()){
- System.out.println(m.group(1)); //我們只要擷取的群組1的數字即可。結果 3434
- System.out.println(m.group(0)); // 0組是整個運算式,看這裡,並沒有提煉出(?<!c)的字元 。結果 a3434bd
- }
可以看到,非擷取的群組,最後是不會返回結果的,因為它本身並不捕獲文本。
最後用了兩種方法解決:
解法一:比較笨的方法,沒有使用非擷取的群組:
Pattern p = Pattern.compile("<(.*)>.*</\\1>");……
Matcher m = p.matcher(line);
HashSet setstr = new HashSet();
while( m.find()){
if( !setstr.contains(m.group(1)) && !setstr.isEmpty()){
//這裡的line就是符合要求的內容
解法二:使用非擷取的群組
Pattern p = Pattern.compile("<(.*)>.*</\\1>.*<(?!\\1)");
一個Regex就可解決。
再次印證了Regex的重要性。
http://www.inmvc.com (IN-MVC開源架構,JAVA EE架構師實戰)