在日常Java后端開發過程中,免不了對數據字段的解析,自然就少不了對字符串的操作,這其中就包含了正則表達式這一塊的內容,這里面涉及Java包中Pattern類和Macher類,本篇博客就針對這一塊內容和常見的用法進行總結,本博客主要的參考資料是《Java編程思想》第4版。
以一個問題引出本博客的內容。問題是:檢查一個字符串是否以大寫字母開頭,以句號結尾。
1
2
3
4
|
String len= "^[A-Z].*[\\.]$" ; System.out.println( "Taaa." .matches(len)); //true System.out.println( "taaa." .matches(len)); //false System.out.println( "Taaa" .matches(len)); //false |
1.*[\.] 就 是 正 則 表 達 式 , 用 來 匹 配 字 符 串 。 代 表 一 行 的 起 始 , [ A − Z ] 表 是 A 到 Z 任 何 的 字 母 , 就是正則表達式,用來匹配字符串。^代表一行的起始,[A-Z]表是A到Z任何的字母, 就是正則表達式,用來匹配字符串。代表一行的起始,[A−Z]表是A到Z任何的字母,表示的一行的結束。
一、規則表
規則表定義正則表達式該怎么寫。這類東西不需要刻意去記,用的時候去寫就好,用多了自然就記住了。
1.字符
B | 指定字符B |
---|---|
\xhh | 十六進制為oxhh的字符 |
\uhhhh | 十六進制表示為oxhhhh的Unicode字符 |
\t | 制表符Tab |
\n | 換行符 |
\r | 回車 |
\f | 換頁 |
\e | 轉義 |
2.字符類 .
. | 任意字符 |
---|---|
[abc] | 包含a、b和c的任何字符(和a|b|c作用相同) |
[^abc] | 除了a、b和c之外的任何字符(否定) |
[a-zA-Z] | 從a到z或從A到Z的任何字符(范圍) |
[abc[hij]] | 任意a、b、c、h、i和j字符(與a|b|c|h|i|j作用相同) |
[a-z&&[hij]] | 任意h、i或j |
\s | 空白符(空格、tab、換行、換頁和回車) |
\S | 非空白符 |
\d | 數字 |
\D | 非數字 |
\w | 詞字符[a-zA-Z0-9] |
\W | 非詞字符 |
3.邊界匹配符
^ | 一行的起始 |
---|---|
$ | 一行的結束 |
\b | 詞的邊界 |
\B | 非詞的邊界 |
\G | 前一個匹配的結束 |
4.邏輯操作符
XY | Y跟在X后面 |
---|---|
X|Y | X或Y |
(X) | 捕獲組 |
5.量詞
貪婪型 | 勉強型 | 占有型 | 如何匹配 |
---|---|---|---|
X? | X?? | X?+ | 一個或零個X |
X* | X*? | X*+ | 零個或多個X |
X+ | X+? | X++ | 一個或多個X |
X{n} | X{n}? | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | X至少n次,且不超過m次 |
二、Pattern類
1.Pattern類的實例獲取—compile方法
1.下面主要是Pattern類源碼下一些重要的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private Pattern(String p, int f) { pattern = p; flags = f; // to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present if ((flags & UNICODE_CHARACTER_CLASS) != 0 ) flags |= UNICODE_CASE; // Reset group index count capturingGroupCount = 1 ; localCount = 0 ; if (!pattern.isEmpty()) { try { compile(); } catch (StackOverflowError soe) { throw error( "Stack overflow during pattern compilation" ); } } else { root = new Start(lastAccept); matchRoot = lastAccept; } } |
從Pattern類代碼可以看出構造器是私有的,故無法使用new去獲得Pattern類對象實例。
仔細查詢Pattern類下代碼后發現通過靜態方法compile去獲取Pattern類的實例對象。
1
2
3
|
public static Pattern compile(String regex) { return new Pattern(regex, 0 ); } |
通過compile方法生成一個Pattern類實例對象,然后將想要匹配的字段傳入Pattern對象的matcher()方法,matcher()方法會返回生成一個Matcher類實例對象,這個對象也有很多對應的方法。所以Matcher類也是無法通過new去創建一個實例的,而是通過matcher方法
1
2
3
4
5
6
7
8
9
10
|
public Matcher matcher(CharSequence input) { if (!compiled) { synchronized ( this ) { if (!compiled) compile(); } } Matcher m = new Matcher( this , input); return m; } |
1
2
3
4
5
|
public static boolean matches(String regex, CharSequence input) { Pattern p = Pattern.compile(regex); Matcher m = p.matcher(input); return m.matches(); } |
所以之前博客開頭那個問題,也可以用另外兩種方式實現:
方式一:
1
2
3
4
|
Pattern len = Pattern.compile( "^[A-Z].*[\\.]$" ); Matcher matcher = len.matcher( "Taaa." ); boolean matches = matcher.matches(); System.out.println(matches); |
當是從源碼中可以看出matches方法是類靜態方法,所以沒必要使用Macher類實例對象來調用方法。可以使用:
方式二:
1
|
System.out.println(Pattern.matches( "^[A-Z].*[\\.]$" , "Taaa." )); |
2.split方法
下面是Split方法的源碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public String[] split(CharSequence input, int limit) { int index = 0 ; boolean matchLimited = limit > 0 ; ArrayList<String> matchList = new ArrayList<>(); Matcher m = matcher(input); // Add segments before each match found while (m.find()) { if (!matchLimited || matchList.size() < limit - 1 ) { if (index == 0 && index == m.start() && m.start() == m.end()) { // no empty leading substring included for zero-width match // at the beginning of the input char sequence. continue ; } String match = input.subSequence(index, m.start()).toString(); matchList.add(match); index = m.end(); } else if (matchList.size() == limit - 1 ) { // last one String match = input.subSequence(index, input.length()).toString(); matchList.add(match); index = m.end(); } } // If no match was found, return this if (index == 0 ) return new String[] {input.toString()}; // Add remaining segment if (!matchLimited || matchList.size() < limit) matchList.add(input.subSequence(index, input.length()).toString()); // Construct result int resultSize = matchList.size(); if (limit == 0 ) while (resultSize > 0 && matchList.get(resultSize- 1 ).equals( "" )) resultSize--; String[] result = new String[resultSize]; return matchList.subList( 0 , resultSize).toArray(result); } |
方法解析:
input
:要拆分的字符序列;
limit
:結果閾值;根據指定模式拆分輸入序列。
limit參數作用:
limit參數控制應用模式的次數,從而影響結果數組的長度。
如果 n 大于零
,那么模式至多應用 n- 1 次,數組的長度不大于 n,并且數組的最后條目將包含除最后的匹配定界符之外的所有輸入。
如果 n 非正
,那么將應用模式的次數不受限制,并且數組可以為任意長度。
如果 n 為零
,那么應用模式的次數不受限制,數組可以為任意長度,并且將丟棄尾部空字符串。
詳細講解:假設 input=“boo:and:foo”,匹配符為"o",可知模式最多可應用4次,數組的長度最大為5;
1、當limit=-2時,應用模式的次數不受限制且數組可以為任意長度;推測模式應用4次,數組的長度為5,數組為{“b”,"",":and:f","",""};
2、當limit=2時,模式至多應用1次,數組的長度不大于 2,且第二個元素包含除最后的匹配定界符之外的所有輸入;推測模式應用1次,數組的長度為2,數組為{“b”,“o:and:foo”};
3、當limit=7時,模式至多應用6次,數組的長度不大于 7;推測模式應用4次,數組的長度為5,數組為{“b”,"",":and:f","",""};
4、當limit=0時,應用模式的次數不受限制,數組可以為任意長度,并且將丟棄尾部空字符串;推測模式應用4次,數組的長度為3,數組為{“b”,"",":and:f"}。
這里m.find像迭代器,遍歷輸入字符串
3.Pattern中匹配標記參數
編譯標記 | 效果 |
---|---|
Pattern.CANON_EQ | 啟用規范等價。當且僅當兩個字符的“正規分解(canonicaldecomposition)”都完全相同的情況下,才認定匹配。默認情況下,不考慮“規范相等性(canonical equivalence)”。 |
Pattern.CASE_INSENSITIVE | 啟用不區分大小寫的匹配。默認情況下,大小寫不敏感的匹配只適用于US-ASCII字符集。這個標志能讓表達式忽略大小寫進行匹配,要想對Unicode字符進行大小不敏感的匹配,只要將UNICODE_CASE與這個標志合起來就行了。 |
Pattern.COMMENTS | 模式中允許空白和注釋。在這種模式下,匹配時會忽略(正則表達式里的)空格字符(不是指表達式里的“\s”,而是指表達式里的空格,tab,回車之類)。注釋從#開始,一直到這行結束。可以通過嵌入式的標志來啟用Unix行模式。 |
Pattern.DOTALL | 啟用dotall模式。在這種模式下,表達式‘.'可以匹配任意字符,包括表示一行的結束符。默認情況下,表達式‘.'不匹配行的結束符。 |
Pattern.MULTILINE | 啟用多行模式。在這種模式下,‘^'和‘ ' 分 別 匹 配 一 行 的 開 始 和 結 束 。 此 外 , ‘ ' 仍 然 匹 配 字 符 串 的 開 始 , ‘ '分別匹配一行的開始和結束。此外,‘^'仍然匹配字符串的開始,‘ '分別匹配一行的開始和結束。此外,‘'仍然匹配字符串的開始,‘'也匹配字符串的結束。默認情況下,這兩個表達式僅僅匹配字符串的開始和結束。 |
Pattern.UNICODE_CASE | 啟用Unicode感知的大小寫折疊。在這個模式下,如果你還啟用了CASE_INSENSITIVE標志,那么它會對Unicode字符進行大小寫不敏感的匹配。默認情況下,大小寫不敏感的匹配只適用于US-ASCII字符集。 |
Pattern.UNIX_LINES | 啟用Unix行模式。在這個模式下,只有‘\n'才被認作一行的中止,并且與‘.'、‘^'、以及‘$'進行匹配。 |
Pattern.CASE_INSENSITIVE
、Pattern.COMMENTS
、Pattern.MULTILINE
這三個比較常用。
三、Matcher類
對照Matcher構造器源碼,可知構造器將Pattern對象的引用賦于Matcher中變量parentPattern,目標字符串賦于變量text;并創建了數組groups和locals 。
數組groups是組使用的存儲。存儲的是當前匹配的各捕獲組的first和last信息。
groups[0]存儲的是組零的first,groups[1]存儲的是組零的last,groups[2]存儲的是組1的first,groups[3]存儲的是組1的last,依次類推。
Matcher類的方法非常多,這里就不對每個方法的源碼進行詳細的解讀了,后續如果有空會深入研究一下。將常用方法總結如下:
方法名 | 功能作用 |
---|---|
public int groupCount() | 返回此匹配器中的捕獲組數 |
public String group() | 實際上是調用了group(int group) ,只不過參數是0 |
public String group(int group) | 返回當前查找而獲得的與組匹配的所有子串內容 |
public int start() | 返回當前匹配的子串的第一個字符在目標字符串中的索引位置 |
public int start(int group) | 返回當前匹配的指定組中的子串的第一個字符在目標字符串中的索引位置 。 |
public int end() | 返回當前匹配的子串的最后一個字符的下一個位置在目標字符串中的索引位置 。 |
public int end(int group) | 返回當前匹配的的指定組中的子串的最后一個字符的下一個位置在目標字符串中的索引位置 |
public boolean find() | 在目標字符串里查找下一個匹配子串 |
public boolean find(int start) | 重置此匹配器,然后嘗試查找匹配該模式,從指定的位置開始查找下一個匹配的子串 |
public int regionStart() | 報告此匹配器區域的開始索引。 |
public int regionEnd() | 報告此匹配器區域的結束索引(不包括)。 |
public Matcher region(int start,int end) | 設置此匹配器的區域限制。重置匹配器,然后設置區域,使其從start參數指定的索引開始,到 end參數指定的索引結束(不包括end索引處的字符)。 |
public boolean lookingAt() | 從目標字符串開始位置進行匹配。只有在有匹配且匹配的某一子串中包含目標字符串第一個字符的情況下才會返回true。 |
public boolean matches() | 只有完全匹配時才會返回true。 |
public Matcher appendReplacement(StringBuffer sb, String replacement) | 將當前匹配子串替換為指定字符串,并將從上次匹配結束后到本次匹配結束后之間的字符串添加到一個StringBuffer對象中,最后返回其字符串表示形式。 |
public StringBuffer appendTail(StringBuffer sb) | 將最后一次匹配工作后剩余的字符串添加到一個StringBuffer對象里。 |
public String replaceAll(String replacement) | 將匹配的子串用指定的字符串替換。 |
public String replaceFirst(String replacement) | 將匹配的第一個子串用指定的字符串替換。 |
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!
原文鏈接:https://blog.csdn.net/weixin_41938314/article/details/120124014