Java字符串 正則表達(dá)式詳解
在日常Java后端開發(fā)過程中,免不了對(duì)數(shù)據(jù)字段的解析,自然就少不了對(duì)字符串的操作,這其中就包含了正則表達(dá)式這一塊的內(nèi)容,這里面涉及Java包中Pattern類和Macher類,本篇博客就針對(duì)這一塊內(nèi)容和常見的用法進(jìn)行總結(jié),本博客主要的參考資料是《Java編程思想》第4版。
以一個(gè)問題引出本博客的內(nèi)容。問題是:檢查一個(gè)字符串是否以大寫字母開頭,以句號(hào)結(jié)尾。
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.*[\.] 就 是 正 則 表 達(dá) 式 , 用 來 匹 配 字 符 串 。 代 表 一 行 的 起 始 , [ A − Z ] 表 是 A 到 Z 任 何 的 字 母 , 就是正則表達(dá)式,用來匹配字符串。^代表一行的起始,[A-Z]表是A到Z任何的字母, 就是正則表達(dá)式,用來匹配字符串。代表一行的起始,[A−Z]表是A到Z任何的字母,表示的一行的結(jié)束。
一、規(guī)則表
規(guī)則表定義正則表達(dá)式該怎么寫。這類東西不需要刻意去記,用的時(shí)候去寫就好,用多了自然就記住了。
1.字符
B | 指定字符B |
---|---|
\xhh | 十六進(jìn)制為oxhh的字符 |
\uhhhh | 十六進(jìn)制表示為oxhhhh的Unicode字符 |
\t | 制表符Tab |
\n | 換行符 |
\r | 回車 |
\f | 換頁 |
\e | 轉(zhuǎn)義 |
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 | 數(shù)字 |
\D | 非數(shù)字 |
\w | 詞字符[a-zA-Z0-9] |
\W | 非詞字符 |
3.邊界匹配符
^ | 一行的起始 |
---|---|
$ | 一行的結(jié)束 |
\b | 詞的邊界 |
\B | 非詞的邊界 |
\G | 前一個(gè)匹配的結(jié)束 |
4.邏輯操作符
XY | Y跟在X后面 |
---|---|
X|Y | X或Y |
(X) | 捕獲組 |
5.量詞
貪婪型 | 勉強(qiáng)型 | 占有型 | 如何匹配 |
---|---|---|---|
X? | X?? | X?+ | 一個(gè)或零個(gè)X |
X* | X*? | X*+ | 零個(gè)或多個(gè)X |
X+ | X+? | X++ | 一個(gè)或多個(gè)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類的實(shí)例獲取—compile方法
1.下面主要是Pattern類源碼下一些重要的方法
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類代碼可以看出構(gòu)造器是私有的,故無法使用new去獲得Pattern類對(duì)象實(shí)例。
仔細(xì)查詢Pattern類下代碼后發(fā)現(xiàn)通過靜態(tài)方法compile去獲取Pattern類的實(shí)例對(duì)象。
public static Pattern compile(String regex) { return new Pattern(regex, 0); }
通過compile方法生成一個(gè)Pattern類實(shí)例對(duì)象,然后將想要匹配的字段傳入Pattern對(duì)象的matcher()方法,matcher()方法會(huì)返回生成一個(gè)Matcher類實(shí)例對(duì)象,這個(gè)對(duì)象也有很多對(duì)應(yīng)的方法。所以Matcher類也是無法通過new去創(chuàng)建一個(gè)實(shí)例的,而是通過matcher方法
public Matcher matcher(CharSequence input) { if (!compiled) { synchronized(this) { if (!compiled) compile(); } } Matcher m = new Matcher(this, input); return m; }
public static boolean matches(String regex, CharSequence input) { Pattern p = Pattern.compile(regex); Matcher m = p.matcher(input); return m.matches(); }
所以之前博客開頭那個(gè)問題,也可以用另外兩種方式實(shí)現(xiàn):
方式一:
Pattern len = Pattern.compile("^[A-Z].*[\\.]$"); Matcher matcher = len.matcher("Taaa."); boolean matches = matcher.matches(); System.out.println(matches);
當(dāng)是從源碼中可以看出matches方法是類靜態(tài)方法,所以沒必要使用Macher類實(shí)例對(duì)象來調(diào)用方法??梢允褂茫?/p>
方式二:
System.out.println(Pattern.matches("^[A-Z].*[\\.]$","Taaa."));
2.split方法
下面是Split方法的源碼:
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
:結(jié)果閾值;根據(jù)指定模式拆分輸入序列。
limit參數(shù)作用:
limit參數(shù)控制應(yīng)用模式的次數(shù),從而影響結(jié)果數(shù)組的長度。
如果 n 大于零
,那么模式至多應(yīng)用 n- 1 次,數(shù)組的長度不大于 n,并且數(shù)組的最后條目將包含除最后的匹配定界符之外的所有輸入。
如果 n 非正
,那么將應(yīng)用模式的次數(shù)不受限制,并且數(shù)組可以為任意長度。
如果 n 為零
,那么應(yīng)用模式的次數(shù)不受限制,數(shù)組可以為任意長度,并且將丟棄尾部空字符串。
詳細(xì)講解:假設(shè) input=“boo:and:foo”,匹配符為"o",可知模式最多可應(yīng)用4次,數(shù)組的長度最大為5;
1、當(dāng)limit=-2時(shí),應(yīng)用模式的次數(shù)不受限制且數(shù)組可以為任意長度;推測(cè)模式應(yīng)用4次,數(shù)組的長度為5,數(shù)組為{“b”,"",":and:f","",""};
2、當(dāng)limit=2時(shí),模式至多應(yīng)用1次,數(shù)組的長度不大于 2,且第二個(gè)元素包含除最后的匹配定界符之外的所有輸入;推測(cè)模式應(yīng)用1次,數(shù)組的長度為2,數(shù)組為{“b”,“o:and:foo”};
3、當(dāng)limit=7時(shí),模式至多應(yīng)用6次,數(shù)組的長度不大于 7;推測(cè)模式應(yīng)用4次,數(shù)組的長度為5,數(shù)組為{“b”,"",":and:f","",""};
4、當(dāng)limit=0時(shí),應(yīng)用模式的次數(shù)不受限制,數(shù)組可以為任意長度,并且將丟棄尾部空字符串;推測(cè)模式應(yīng)用4次,數(shù)組的長度為3,數(shù)組為{“b”,"",":and:f"}。
這里m.find像迭代器,遍歷輸入字符串
3.Pattern中匹配標(biāo)記參數(shù)
編譯標(biāo)記 | 效果 |
---|---|
Pattern.CANON_EQ | 啟用規(guī)范等價(jià)。當(dāng)且僅當(dāng)兩個(gè)字符的“正規(guī)分解(canonicaldecomposition)”都完全相同的情況下,才認(rèn)定匹配。默認(rèn)情況下,不考慮“規(guī)范相等性(canonical equivalence)”。 |
Pattern.CASE_INSENSITIVE | 啟用不區(qū)分大小寫的匹配。默認(rèn)情況下,大小寫不敏感的匹配只適用于US-ASCII字符集。這個(gè)標(biāo)志能讓表達(dá)式忽略大小寫進(jìn)行匹配,要想對(duì)Unicode字符進(jìn)行大小不敏感的匹配,只要將UNICODE_CASE與這個(gè)標(biāo)志合起來就行了。 |
Pattern.COMMENTS | 模式中允許空白和注釋。在這種模式下,匹配時(shí)會(huì)忽略(正則表達(dá)式里的)空格字符(不是指表達(dá)式里的“\s”,而是指表達(dá)式里的空格,tab,回車之類)。注釋從#開始,一直到這行結(jié)束??梢酝ㄟ^嵌入式的標(biāo)志來啟用Unix行模式。 |
Pattern.DOTALL | 啟用dotall模式。在這種模式下,表達(dá)式‘.'可以匹配任意字符,包括表示一行的結(jié)束符。默認(rèn)情況下,表達(dá)式‘.'不匹配行的結(jié)束符。 |
Pattern.MULTILINE | 啟用多行模式。在這種模式下,‘^'和‘ ' 分 別 匹 配 一 行 的 開 始 和 結(jié) 束 。 此 外 , ‘ ' 仍 然 匹 配 字 符 串 的 開 始 , ‘ '分別匹配一行的開始和結(jié)束。此外,‘^'仍然匹配字符串的開始,‘ '分別匹配一行的開始和結(jié)束。此外,‘'仍然匹配字符串的開始,‘'也匹配字符串的結(jié)束。默認(rèn)情況下,這兩個(gè)表達(dá)式僅僅匹配字符串的開始和結(jié)束。 |
Pattern.UNICODE_CASE | 啟用Unicode感知的大小寫折疊。在這個(gè)模式下,如果你還啟用了CASE_INSENSITIVE標(biāo)志,那么它會(huì)對(duì)Unicode字符進(jìn)行大小寫不敏感的匹配。默認(rèn)情況下,大小寫不敏感的匹配只適用于US-ASCII字符集。 |
Pattern.UNIX_LINES | 啟用Unix行模式。在這個(gè)模式下,只有‘\n'才被認(rèn)作一行的中止,并且與‘.'、‘^'、以及‘$'進(jìn)行匹配。 |
Pattern.CASE_INSENSITIVE
、Pattern.COMMENTS
、Pattern.MULTILINE
這三個(gè)比較常用。
三、Matcher類
對(duì)照Matcher構(gòu)造器源碼,可知構(gòu)造器將Pattern對(duì)象的引用賦于Matcher中變量parentPattern,目標(biāo)字符串賦于變量text;并創(chuàng)建了數(shù)組groups和locals 。
數(shù)組groups是組使用的存儲(chǔ)。存儲(chǔ)的是當(dāng)前匹配的各捕獲組的first和last信息。
groups[0]存儲(chǔ)的是組零的first,groups[1]存儲(chǔ)的是組零的last,groups[2]存儲(chǔ)的是組1的first,groups[3]存儲(chǔ)的是組1的last,依次類推。
Matcher類的方法非常多,這里就不對(duì)每個(gè)方法的源碼進(jìn)行詳細(xì)的解讀了,后續(xù)如果有空會(huì)深入研究一下。將常用方法總結(jié)如下:
方法名 | 功能作用 |
---|---|
public int groupCount() | 返回此匹配器中的捕獲組數(shù) |
public String group() | 實(shí)際上是調(diào)用了group(int group) ,只不過參數(shù)是0 |
public String group(int group) | 返回當(dāng)前查找而獲得的與組匹配的所有子串內(nèi)容 |
public int start() | 返回當(dāng)前匹配的子串的第一個(gè)字符在目標(biāo)字符串中的索引位置 |
public int start(int group) | 返回當(dāng)前匹配的指定組中的子串的第一個(gè)字符在目標(biāo)字符串中的索引位置 。 |
public int end() | 返回當(dāng)前匹配的子串的最后一個(gè)字符的下一個(gè)位置在目標(biāo)字符串中的索引位置 。 |
public int end(int group) | 返回當(dāng)前匹配的的指定組中的子串的最后一個(gè)字符的下一個(gè)位置在目標(biāo)字符串中的索引位置 |
public boolean find() | 在目標(biāo)字符串里查找下一個(gè)匹配子串 |
public boolean find(int start) | 重置此匹配器,然后嘗試查找匹配該模式,從指定的位置開始查找下一個(gè)匹配的子串 |
public int regionStart() | 報(bào)告此匹配器區(qū)域的開始索引。 |
public int regionEnd() | 報(bào)告此匹配器區(qū)域的結(jié)束索引(不包括)。 |
public Matcher region(int start,int end) | 設(shè)置此匹配器的區(qū)域限制。重置匹配器,然后設(shè)置區(qū)域,使其從start參數(shù)指定的索引開始,到 end參數(shù)指定的索引結(jié)束(不包括end索引處的字符)。 |
public boolean lookingAt() | 從目標(biāo)字符串開始位置進(jìn)行匹配。只有在有匹配且匹配的某一子串中包含目標(biāo)字符串第一個(gè)字符的情況下才會(huì)返回true。 |
public boolean matches() | 只有完全匹配時(shí)才會(huì)返回true。 |
public Matcher appendReplacement(StringBuffer sb, String replacement) | 將當(dāng)前匹配子串替換為指定字符串,并將從上次匹配結(jié)束后到本次匹配結(jié)束后之間的字符串添加到一個(gè)StringBuffer對(duì)象中,最后返回其字符串表示形式。 |
public StringBuffer appendTail(StringBuffer sb) | 將最后一次匹配工作后剩余的字符串添加到一個(gè)StringBuffer對(duì)象里。 |
public String replaceAll(String replacement) | 將匹配的子串用指定的字符串替換。 |
public String replaceFirst(String replacement) | 將匹配的第一個(gè)子串用指定的字符串替換。 |
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringBoot如何整合mybatis-generator-maven-plugin 1.4.0
這篇文章主要介紹了SpringBoot整合mybatis-generator-maven-plugin 1.4.0的實(shí)現(xiàn)方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-01-01Springboot+AOP實(shí)現(xiàn)返回?cái)?shù)據(jù)提示語國際化的示例代碼
這篇文章主要介紹了Springboot+AOP實(shí)現(xiàn)返回?cái)?shù)據(jù)提示語國際化的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-07-07SpringCloud基于Feign的可編程式接口調(diào)用實(shí)現(xiàn)
本文主要介紹了SpringCloud基于Feign的可編程式接口調(diào)用實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04java時(shí)間戳轉(zhuǎn)日期格式的實(shí)現(xiàn)代碼
本篇文章是對(duì)java時(shí)間戳轉(zhuǎn)日期格式的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(57)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08Java使用Condition控制線程通信的方法實(shí)例詳解
這篇文章主要介紹了Java使用Condition控制線程通信的方法,結(jié)合實(shí)例形式分析了使用Condition類同步檢測(cè)控制線程通信的相關(guān)操作技巧,需要的朋友可以參考下2019-09-09Java通過Socket實(shí)現(xiàn)簡單多人聊天室
這篇文章主要為大家詳細(xì)介紹了Java通過Socket實(shí)現(xiàn)簡單多人聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04