原文地址:https://zhuanlan.zhihu.com/p/59904349
字符匹配符與範圍控制符
- . (點) 任意字符
- \s (反斜槓加小寫字母s) 空白字符: 回車,製表,空格,換行
- \S (反斜槓加大寫字母S) 非空白字符, 除了上面那四個空白字符
- \w (反斜槓加小寫字母w) 單詞字符: 小寫字母a-z, 大寫字母A-Z, 數字0-9, 下劃線 _
- \W (反斜槓加大寫字母W) 非單詞字符,除了上面提到的那些單詞字符
- [ ] (中括號) 範圍字符
- [abc] 表示字母abc中的任意一個
- [a-z] 表示字母a到z中的任意一個
- [^abc] ^ 爲取非, 除了abc, 剩下的全部字符
- [abc[def]] 這種寫法爲並集,意爲abcdef中的任意一個
- [abc&[bce]] & 表示與, 也就是交集。 abc 與上 bce, 最終結果爲bc
數量控制符
上面介紹的字符匹配符和範圍控制符,一般是與數量控制符組合使用,才能發揮真正威力。 而數量控制符號,分爲貪婪型,勉強型,和佔有型三種類型,每種類型在不同的場合有不同的用法。
貪婪型
- ? 表示1個或0個。換句話說,表示要不然沒有,要不然只有1個
- * 表示0個或多個。
- + 表示1個或多個。
- {n} 表示正好n個
- {n,m} 表示n到m個,這是一個左閉右閉區間
- {n,} 表示至少n個
勉強型
相比於貪婪型,勉強型只是多了一個?:
- ?? 表示1個或0個。換句話說,表示要不然沒有,要不然只有1個
- *? 表示0個或多個。
- +? 表示1個或多個。
- {n}? 表示正好n個
- {n,m}? 表示n到m個,這是一個左閉右閉區間
- {n,}? 表示至少n個
佔有型
相比於貪婪型,佔有型多了一個 +
- ?+ 表示1個或0個。換句話說,表示要不然沒有,要不然只有1個
- *+ 表示0個或多個。
- ++ 表示1個或多個。
- {n}+ 表示正好n個
- {n,m}+ 表示n到m個,這是一個左閉右閉區間
- {n,}+ 表示至少n個
不同類型的區別
現在詳細分析 貪婪型、勉強型和佔有型的區別, 舉例如下:
String s = "abcd&"; // 目標字符串
String regular = "\\w+\\w"; // 貪婪型正則
String regular2 = "\\w+?\\w"; // 勉強型正則
String regular3 = "\\w++\\w"; // 佔有型正則
Matcher m = Pattern.compile(regular).matcher(s);
// 調用方法去匹配字符串
if(m.find()) {
System.out.println(m.group());
}
分析:
- 貪婪型。\w+, 意爲匹配一個或多個單詞字符,對於貪婪型,將盡量多的匹配字符,abcd全部匹配出來,直到遇到特殊字符&,匹配結束,然後用\w 去匹配特殊字符&,發現並不匹配,此時將會回溯,也就是說\w+只匹配abc三位,然後讓最後一個\w去匹配d,最終\\w+\\w匹配出結果abcd.
- 勉強型。對於勉強型,會盡量少的匹配字符。既然,+表示一個或多個,那就只匹配一個,所以\w+?最終只匹配出一個a, 然後\w再去匹配一個b,最終匹配結果爲ab。
- 佔有型。 佔有型和貪婪型很像,剛開始也會盡量多的匹配字符,但是佔有型沒有回溯機制,遇到不匹配的內容後,將自動停止而不是向前回溯。 所以 \\w++,自動匹配到abcd之後,遇到了&,發現不匹配,然後自動停止了,此時還有一個\w也和&不匹配,所以最終結果匹配失敗,輸出爲空。
位置匹配符
位置匹配符,不匹配任何字符只匹配位置。也就是說,位置匹配符是零寬度的,只匹配位置。
- ^ 匹配一行的開始位置
- $ 匹配一行的結束位置
- \b 匹配單詞邊界。 這裏的單詞指的就是, a-z, 0-9, _, 和\w不同的是,\b匹配的單詞還包括漢字。 單詞邊界匹配,是匹配單詞字符和非單詞字符之間的位置。
- \B 匹配非單詞邊界。和\b,剛好相反, \B 匹配單詞與單詞之間的位置,或者非單詞與非單詞之間的位置。
- \G 匹配前一個匹配結束時的位置。
舉例說明:
String s = "a=124_a=789_a=490";
String regular = "^a=\\d+";
比如說只想把開頭的a=124找出來,而不關心後面的a,此時就可以用位置匹配符。^匹配一行的開始位置,a= 去匹配 a=, \\d+匹配出124, 所以最終可正確匹配出開始位置的a=124
還有一類特殊的匹配符,雖然嚴格意義上來說不能算是位置匹配符,但是由於這類匹配符也是零寬度的,所以本文章把這類匹配符歸爲位置匹配符。 這類特殊的匹配符,稱爲零寬度斷言匹配。
- (?=exp) 零寬度向前匹配
- (?<=exp) 零寬度向後匹配
- (?!exp) 零寬度向前非匹配
- (?<!exp) 零寬度向後非匹配
舉例說明2:
// 目標字符串。 只想取出2前面的abc
String s = "abc1 abc1 abc2 abc3";
String regular = "\\w{3}(?=2)";
Matcher m = Pattern.compile(regular).matcher(s);
while(m.find()) {
System.out.println(m.group());
}
就像是$匹配字符串結尾一樣,(?=2) 也是匹配字符串中爲2的位置,然後再去檢查(?=2)前面的\\w{3} 是否匹配abc, 匹配則輸出結果。 之所以說是零寬度,是因爲(?=2)只是起一個錨點定位作用,此時尾指針指向的不是2後面,而是c後面。
再比如說3:
String s = "aaaaaaaa";
String regular = "aa(?=aa)";
Matcher m = Pattern.compile(regular).matcher(s);
while(m.find()) {
System.out.println(m.group());
}
結果輸出將是: aa aa aa
因爲,第1次匹配,匹配到前4個a,輸出aa; 第2次匹配,是從第2個a後開始向後,而不是第4個a後面,(?=aa) 只標定位置,不佔有寬度; 以此類推,最終結果輸出爲aa aa aa
舉例說明4:
對於零寬度向前匹配,和向後匹配方向相反:
// 目標字符串。 只要求輸出2後面的abc
String s = "1abc 2abc 3abc";
String regular = "(?<=2)\\w{3}";
Matcher m = Pattern.compile(regular).matcher(s);
while(m.find()) {
System.out.println(m.group());
}
只要求輸出2後面的abc,那麼就可以用(?<=2)來進行錨點標定,匹配出其後面的字符串。
對於(?!exp) 和 (?<!exp) 這兩個非匹配斷言,和匹配斷言正好相反。 (?!exp) 是要求匹配內容的後面,不能有指定的內容; (?<!exp) 要求匹配的前面不能有指定的內容。
模式說明符
- (?i) 開啓大小寫忽略模式,但是隻適用於ASCII字符
- (?u) 開啓utf-8編碼模式
- (?m) 開啓多行匹配模式,.不匹配空白字符
- (?s) 單行模式,.匹配任意字符,包括空白字符
- (?d) 單行模式,.不匹配空白字符
舉例說明:
String test2 = "I care not whether I can succeed\n"
+ "Since in choice of the distance\n"
+ "I simply travel in wind and rain\n"
+ "I care not whether I can win love\n"
+ "Since in deep love with a rose\n"
+ "I just unbosom myself bravely";
String reg = "(?m)\\w{3}$";
在默認情況下,^ 和 $ 只匹配全部字符串的開始與結束位置,但是如果用(?m)開啓多行匹配模式之後, ^ 和 $ 將匹配每一行的開始與結束位置。所以說,上面上面那個舉例正則(?m)\\w{3}$,將會匹配出每一行的最後三個字符。