前段時間做聊天消息的解析,涉及到要在羣消息中查找@的人,需要把@後面的用戶暱稱解析出來,首先想到的當然就是用正則來實現了,於是一頓操作外加度娘加持。
public static List<String> findRegex(String p,String s){
Pattern pattern = Pattern.compile(p);
Matcher matcher = pattern.matcher(s);
List<String> list=new ArrayList<>();
while (matcher.find()) {
list.add(matcher.group());
}
return list;
}
//引用的地方調用
//@開頭,空格結尾
List<String> list=findRegex("(?<=\\@)(\\S+)(?=\\s)",msg);
這裏面涉及知識點:
- Java的兩個類
Pattern
,Matcher
Pattern.compile生成Pattern
find()匹配
group()得到匹配結果
2.正則表達式。
零寬斷言用於匹配@來頭及空格結尾
具體的用法含義請查看下面詳細內容
1.Java中的正則表達式類
java.util.regex 包主要包括以下三個類:
Pattern 類:
pattern 對象是一個正則表達式的編譯表示。
構造
Pattern 類沒有公共構造方法。要創建一個 Pattern 對象,你必須首先調用其公共靜態編譯方法,它返回一個 Pattern 對象。該方法接受一個正則表達式作爲它的第一個參數。
Pattern p=Pattern.compile("\\S+");
p.pattern();//返回Pattern.complile(String regex)的regex參數 \S+
compile( )方法還有一個版本,它需要一個控制正則表達式的匹配行爲的參數:
Pattern Pattern.compile(String regex, int flag)
flag的取值範圍如下:
編譯標誌 | 效果 |
---|---|
Pattern.CANON_EQ | 當且僅當兩個字符的"正規分解(canonical decomposition)"都完全相同的情況下,才認定匹配。比如用了這個標誌之後,表達式"a/u030A"會匹配"?"。默認情況下,不考慮"規範相等性(canonical equivalence)"。 |
Pattern.CASE_INSENSITIVE(?i) | 默認情況下,大小寫不敏感的匹配只適用於US-ASCII字符集。這個標誌能讓表達式忽略大小寫進行匹配。要想對Unicode字符進行大小不明感的匹配,只要將UNICODE_CASE與這個標誌合起來就行了。 |
Pattern.COMMENTS(?x) | 在這種模式下,匹配時會忽略(正則表達式裏的)空格字符(注:不是指表達式裏的"//s",而是指表達式裏的空格,tab,回車之類)。註釋從#開始,一直到這行結束。可以通過嵌入式的標誌來啓用Unix行模式。 |
Pattern.DOTALL(?s) | 在這種模式下,表達式'.'可以匹配任意字符,包括表示一行的結束符。默認情況下,表達式'.'不匹配行的結束符。 |
Pattern.MULTILINE (?m) | 在這種模式下,'^'和''也匹配字符串的結束。默認情況下,這兩個表達式僅僅匹配字符串的開始和結束。 |
Pattern.UNICODE_CASE (?u) | 在這個模式下,如果你還啓用了CASE_INSENSITIVE標誌,那麼它會對Unicode字符進行大小寫不明感的匹配。默認情況下,大小寫不明感的匹配只適用於US-ASCII字符集。 |
Pattern.UNIX_LINES (?d) | 在這個模式下,只有'/n'才被認作一行的中止,並且與'.','^',以及'$'進行匹配。 |
在這些標誌裏面,Pattern.CASE_INSENSITIVE
,Pattern.MULTILINE
,以及Pattern.COMMENTS
是最有用的(其中Pattern.COMMENTS
還能幫我們把思路理清楚,並且/或者做文檔)。注意,你可以用在表達式裏插記號的方式來啓用絕大多數的模式。這些記號就在上面那張表的各個標誌的下面。可以用"OR" ('|')運算符把這些標誌合使用
方法
1.Pattern.split(CharSequence input)
Pattern有一個split(CharSequence input)方法,用於分隔字符串,並返回一個String[],我猜String.split(String regex)就是通過Pattern.split(CharSequence input)來實現的.
Pattern p=Pattern.compile(",");
String[] str=p.split("abc,444,666,ddd,rr");
if(str!=null){
for(int i=0;i<str.length;i++){
System.out.println(String.format("第%d個值爲%s",i,str[i]));
}
}
輸出爲
第0個值爲abc
第1個值爲444
第2個值爲666
第3個值爲ddd
第4個值爲rr
2.Pattern.matcher(String regex,CharSequence input)
是一個靜態方法,用於快速匹配字符串,該方法適合用於只匹配一次,且匹配全部字符串.
Java代碼示例:
Pattern.matches("\\d+","9527");//返回true
Pattern.matches("\\d+","9527aa");//返回false,需要匹配到所有字符串才能返回true,這裏aa不能匹配到
Pattern.matches("\\d+","22bb23");//返回false,需要匹配到所有字符串才能返回true,這裏bb不能匹配到
3.Pattern.matcher(CharSequence input)
說了這麼多,終於輪到Matcher類登場了,Pattern.matcher(CharSequence input)
返回一個Matcher
對象.
Matcher
類的構造方法也是私有的,不能隨意創建,只能通過Pattern.matcher(CharSequence input)
方法得到該類的實例.
Pattern類只能做一些簡單的匹配操作,要想得到更強更便捷的正則匹配操作,那就需要將Pattern與Matcher一起合作.Matcher類提供了對正則表達式的分組支持,以及對正則表達式的多次匹配支持.
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22ssd23");
m.pattern();//返回p 也就是返回該Matcher對象是由哪個Pattern對象的創建的
Matcher 類:
Matcher
對象是對輸入字符串進行解釋和匹配操作的引擎。與Pattern
類一樣,Matcher
也沒有公共構造方法。你需要調用 Pattern
對象的 matcher
方法來獲得一個 Matcher
對象。
匹配操作
Matcher
類提供三個匹配操作方法,三個方法均返回boolean
類型,當匹配到時返回true
,沒匹配到則返回false
-
matches()
對整個字符串進行匹配,只有整個字符串都匹配了才返回true
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.matches();//返回false,因爲bb不能被\d+匹配,導致整個字符串匹配未成功.
Matcher m2=p.matcher("9527");
m2.matches();//返回true,因爲\d+匹配到了整個字符串
我們現在回頭看一下Pattern.matcher(String regex,CharSequence input),它與下面這段代碼等價
Pattern.compile(regex).matcher(input).matches()
-
lookingAt()
對前面的字符串進行匹配,只有匹配到的字符串在最前面才返回true
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.lookingAt();//返回true,因爲\d+匹配到了前面的22
Matcher m2=p.matcher("aa9527");
m2.lookingAt();//返回false,因爲\d+不能匹配前面的aa
-
find()
對字符串進行匹配,匹配到的字符串可以在任何位置.
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("22bb23");
m.find();//返回true
Matcher m2=p.matcher("aa9527");
m2.find();//返回true
Matcher m3=p.matcher("aa9527bb");
m3.find();//返回true
Matcher m4=p.matcher("aabb");
m4.find();//返回false
索引位置
當使用matches()
,lookingAt()
,find()
執行匹配操作後,利用以上三個方法得到更詳細的信息。
-
start()
返回匹配到的子字符串在字符串中的索引位置 -
end()
返回匹配到的子字符串的最後一個字符在字符串中的索引位置 -
group()
返回匹配到的子字符串
Pattern p=Pattern.compile("\\d+");
Matcher m=p.matcher("aaa9527bb");
m.find();//匹配9527
m.start();//返回3
m.end();//返回7,返回的是9527後的索引號
m.group();//返回9527
Mathcer m2=m.matcher("9527bb");
m.lookingAt(); //匹配9527
m.start(); //返回0,由於lookingAt()只能匹配前面的字符串,所以當使用lookingAt()匹配時,start()方法總是返回0
m.end(); //返回4
m.group(); //返回匹配到的結果,即9527
Matcher m3=m.matcher("9527bb");
m.matches(); //匹配整個字符串
m.start(); //返回0,原因相信大家也清楚了
m.end(); //返回6,原因相信大家也清楚了,因爲matches()需要匹配所有字符串
m.group(); //返回9527bb
start()
,end()
,group()
均有一個重載方法它們是start(int i)
,end(int i)
,group(int i)
專用於分組操作,與參數爲0時返回的結果相同,Mathcer
類還有一個groupCount()
用於返回有多少組.
Pattern p=Pattern.compile("([a-z]+)(\\d+)");
Matcher m=p.matcher("aaa9527bb");
m.find(); //匹配aaa9527
m.groupCount(); //返回2,因爲有2組
m.start(0);//與m.start()相同,返回0
m.end(0);//與m.end()相同,返回7
m.start(1); //返回0 返回第一組匹配到的子字符串在字符串中的索引號
m.start(2); //返回3
m.end(1); //返回3 返回第一組匹配到的子字符串的最後一個字符在字符串中的索引位置.
m.end(2); //返回7
m.group(1); //返回aaa,返回第一組匹配到的子字符串
m.group(2); //返回9527,返回第二組匹配到的子字符串
PatternSyntaxException
PatternSyntaxException
是一個非強制異常類,它表示一個正則表達式模式中的語法錯誤。
總結正則:
2、正則表達式語法
元字符
代碼 | 說明 |
---|---|
. | 匹配除換行符以外的任意字符 |
\w | 匹配字母或數字或下劃線或漢字 |
\s | 匹配任意的空白符 |
\d | 匹配數字 |
^ | 匹配字符串的開始 |
$ | 匹配字符串的結束 |
\b | 匹配字符串的結束 |
重複
代碼/語法 | 說明 |
---|---|
* | 重複零次或更多次 |
+ | 重複一次或更多次 |
? | 重複零次或一次 |
{n} | 重複n次 |
{n,} | 重複n次或更多次 |
{n,m} | 重複n到m次 |
字符類
想查找數字,字母或數字,空白是很簡單的,因爲已經有了對應這些字符集合的元字符,但是如果你想匹配沒有預定義元字符的字符集合(比如元音字母a,e,i,o,u),應該怎麼辦?
很簡單,你只需要在方括號裏列出它們就行了,像[aeiou]就匹配任何一個英文元音字母,[.?!]匹配標點符號(.或?或!)。
我們也可以輕鬆地指定一個字符範圍,像[0-9]代表的含意與\d就是完全一致的:一位數字;同理[a-z0-9A-Z_]也完全等同於\w(如果只考慮英文的話)。
分枝條件
用|把不同的規則分別表達。
如:0\d{2}-\d{8}|0\d{3}-\d{7}這個表達式能匹配兩種以連字號分隔的電話號碼:一種是三位區號,8位本地號(如010-12345678),一種是4位區號,7位本地號(0376-2233445)。
反義
代碼/語法 | 說明 |
---|---|
\W | 匹配任意不是字母,數字,下劃線,漢字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非數字的字符 |
\B | 匹配不是單詞開頭或結束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou這幾個字母以外的任意字符 |
分組
重複單個字符直接在字符後面加上限定符就行了,但如果想要重複多個字符又該怎麼辦?你可以用小括號來指定子表達式(也叫做分組),然後你就可以指定這個子表達式的重複次數了,你也可以對子表達式進行其它一些操作。
使用小括號指定一個子表達式後,匹配這個子表達式的文本(也就是此分組捕獲的內容)可以在表達式或其它程序中作進一步的處理。捕獲組可以通過從左到右計算其開括號來編號。例如,在表達式 ((A)(B(C))) 中,存在四個這樣的組:
- ((A)(B(C)))
- (A)
- (B(C))
- (C)
組零始終代表整個表達式。
之所以這樣命名捕獲組是因爲在匹配中,保存了與這些組匹配的輸入序列的每個子序列。捕獲的子序列稍後可以通過 Back 引用在表達式中使用,也可以在匹配操作完成後從匹配器獲取。
與組關聯的捕獲輸入始終是與組最近匹配的子序列。如果由於量化的緣故再次計算了組,則在第二次計算失敗時將保留其以前捕獲的值(如果有的話)例如,將字符串 "aba" 與表達式 (a(b)?)+ 相匹配,會將第二組設置爲 "b"。在每個匹配的開頭,所有捕獲的輸入都會被丟棄。
以 (?) 開頭的組是純的非捕獲 組,它不捕獲文本,也不針對組合計進行計數。
後向引用用於重複搜索前面某個分組匹配的文本。例如:
\b(\w+)\b\s+\1\b
可以用來匹配重複的單詞,像go go, 或者kitty kitty。
也可以自己指定子表達式的組名。要指定一個子表達式的組名,請使用這樣的語法:(?<Word>\w+)
(或者把尖括號換成'也行:(?'Word'\w+)
),這樣就把\w+
的組名指定爲Word
了。要反向引用這個分組捕獲的內容,你可以使用\k<Word>
,所以上一個例子也可以寫成這樣:\b(?<Word>\w+)\b\s+\k<Word>\b
。
零寬斷言
(?=exp)也叫零寬度正預測先行斷言,它斷言被匹配的字符串以表達式exp結尾但除了結尾以外的部分。比如\b\w+(?=ing\b),匹配以ing結尾的單詞的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.時,它會匹配sing和danc。
(?<=exp)也叫零寬度正回顧後發斷言,它斷言自身出現的位置的前面能匹配表達式exp。比如(?<=\bre)\w+\b會匹配以re開頭的單詞的後半部分(除了re以外的部分),例如在查找reading a book時,它匹配ading。
代碼/語法 | 說明 |
---|---|
(?=exp) | 匹配exp前面的位置 |
(?<=exp) | 匹配exp後面的位置 |
(?!exp) | 匹配後面跟的不是exp的位置 |
(?<!exp) | 匹配前面不是exp的位置 |
註釋
小括號的另一種用途是通過語法(?#comment)來包含註釋。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d \d?(?#0-199)
。
貪婪與懶惰
語法 | 說明 |
---|---|
*? | 重複任意次,但儘可能少重複 |
+? | 重複1次或更多次,但儘可能少重複 |
?? | 重複0次或1次,但儘可能少重複 |
{n,m}? | 重複n到m次,但儘可能少重複 |
{n,}? | 重複n次以上,但儘可能少重複 |
當正則表達式中包含能接受重複的限定符時,通常的行爲是(在使整個表達式能得到匹配的前提下)匹配儘可能多的字符。考慮這個表達式:a.b,它將會匹配最長的以a開始,以b結束的字符串。如果用它來搜索aabab的話,它會匹配整個字符串aabab。這被稱爲貪婪匹配。
有時,我們更需要懶惰匹配,也就是匹配儘可能少的字符。前面給出的限定符都可以被轉化爲懶惰匹配模式,只要在它後面加上一個問號?。這樣.?就意味着匹配任意數量的重複,但是在能使整個匹配成功的前提下使用最少的重複。現在看看懶惰版的例子吧:
a.*?b匹配最短的,以a開始,以b結束的字符串。如果把它應用於aabab的話,它會匹配aab(第一到第三個字符)和ab(第四到第五個字符)。
POSIX 字符類(僅 US-ASCII)
語法 | 說明 | |
---|---|---|
\p{Lower} | 小寫字母字符:[a-z] | |
\p{Upper} | 大寫字母字符:[A-Z] | |
\p{ASCII} | 所有 ASCII:[\x00-\x7F] | |
\p{Alpha} | 字母字符:[\p{Lower}\p{Upper}] | |
\p{Digit} | 十進制數字:[0-9] | |
\p{Alnum} | 字母數字字符:[\p{Alpha}\p{Digit}] | |
\p{Punct} | 標點符號:!"#$%&'()*+,-./:;<=>?@[]^_{ | }~ |
\p{Graph} | 可見字符:[\p{Alnum}\p{Punct}] | |
\p{Print} | 可打印字符:[\p{Graph}\x20] | |
\p{Blank} | 空格或製表符:[ \t] | |
\p{Cntrl} | 控制字符:[\x00-\x1F\x7F] | |
\p{XDigit} | 十六進制數字:[0-9a-fA-F] | |
\p{Space} | 空白字符:[ \t\n\x0B\f\r] |
引用
語法 | 說明 |
---|---|
\ | Nothing,但是引用以下字符 |
\Q | Nothing,但是引用所有字符,直到 \E |
\E | Nothing,但是結束從 \Q 開始的引用 |
如:\Q\w+\E表示字符串\w+而不是正則中的單詞字符:[a-zA-Z_0-9]。
其他
語法 | 說明 |
---|---|
\xhh | 十六進制值爲0xhh的字符 |
\uhhhh | 十六進制表示爲0xhhhh的Unicode字符 |
\t | 製表符Tab |
\n | 換行符 |
\r | 回車 |
\f | 換頁 |
\e | 轉義(Escape) |
處理選項
上面介紹了幾個選項如忽略大小寫,處理多行等,這些選項能用來改變處理正則表達式的方式。下面是Java中常用的正則表達式選項:
名稱 | 說明 |
---|---|
CASE_INSENSITIVE | 匹配時區分大小寫 |
MULTILINE | 更改^和的精確含意是:匹配\n之前的位置以及字符串結束前的位置.) |
DOTALL | 在 dotall 模式中,表達式 . 可以匹配任何字符,包括行結束符。默認情況下,此表達式不匹配行結束符。 |
UNICODE_CASE | 指定此標誌後,由 CASE_INSENSITIVE 標誌啓用時,不區分大小寫的匹配將以符合 Unicode Standard 的方式完成。默認情況下,不區分大小寫的匹配假定僅匹配 US-ASCII 字符集中的字符。通過嵌入式標誌表達式 (?u) 也可以啓用 Unicode 感知的大小寫摺疊。指定此標誌可能對性能產生影響。 |
CANON_EQ | 啓用規範等價。指定此標誌後,當且僅當其完整規範分解匹配時,兩個字符纔可視爲匹配。例如,當指定此標誌時,表達式 "a\u030A" 將與字符串 "\u00E5" 匹配。默認情況下,匹配不考慮採用規範等價。不存在可以啓用規範等價的嵌入式標誌字符。指定此標誌可能對性能產生影響。 |
UNIX_LINES | 啓用 Unix 行模式。在此模式中,.、^ 和 $ 的行爲中僅識別 '\n' 行結束符。通過嵌入式標誌表達式 (?d) 也可以啓用 Unix 行模式。 |
LITERAL | 指定此標誌後,指定模式的輸入字符串就會作爲字面值字符序列來對待。輸入序列中的元字符或轉義序列不具有任何特殊意義。標誌 CASE_INSENSITIVE 和 UNICODE_CASE 在與此標誌一起使用時將對匹配產生影響。其他標誌都變得多餘了。不存在可以啓用字面值解析的嵌入式標誌字符。 |
UNICODE_CHARACTER_CLASS | |
COMMENTS | 模式中允許空白和註釋。此模式將忽略空白和在結束行之前以 # 開頭的嵌入式註釋。通過嵌入式標誌表達式 (?x) 也可以啓用註釋模式。 |