Java正則表達式入門學習與實踐
Java正則表達式入門學習與實踐
- 一、正則基礎知識點
- 1、元字符
- 2、重復限定符
- 3、分組
- 4、轉義
- 5、條件或
- 6、區間
- 二、正則進階知識點
- 1、零寬斷言
- 2、零寬斷言測試
- 3、捕獲和非捕獲
- 3、反向引用
- 三、貪婪和非貪婪
- 1、貪婪
- 2、懶惰(非貪婪)
- 四、反義
- 五、 案例
- 1、匹配時間
一、正則基礎知識點
1、元字符
元字符 | 說明 |
---|---|
. | 匹配除換行符以外的任意字符 |
\w | 匹配字母或數字或下劃線或漢字 |
\s | 匹配任意的空白符 |
\d | 匹配數字 |
\b | 匹配單詞的開始或結束 |
^ | 匹配字符串的開始 |
$ | 匹配字符串的結束 |
元字符案例
匹配有abc開頭的字符串
\babc或者^abc
匹配8位數字的QQ號碼
^\d\d\d\d\d\d\d\d$
匹配1開頭11位數字的手機號碼
^1\d\d\d\d\d\d\d\d\d\d$
2、重復限定符
語法 | 說明 |
---|---|
* | 重復零次或更多次 |
+ | 重復一次或更多次 |
? | 重復零次或一次 |
{n} | 重復n次 |
{n,} | 重復n次或更多次 |
{n,m} | 重復n到m次 |
重復限定符案例
匹配8位數字的QQ號碼
在這里插入代碼片
匹配1開頭11位數字的手機號碼
^1\d{10}$
匹配銀行卡號是14~18位的數字
^\d{14,18}$
匹配以a開頭的,0個或多個b結尾的字符串
^ab*$
3、分組
匹配字符串中包含0到多個ab開頭
^(ab)*
4、轉義
如果要匹配的字符串中本身就包含小括號,那是不是沖突?應該怎么辦?
針對這種情況,正則提供了轉義的方式,也就是要把這些元字符、限定符或者關鍵字轉義成普通的字符,做法很簡答,就是在要轉義的字符前面加個斜杠,也就是\即可。
如:要匹配以(ab)開頭
^(\(ab\))*
5、條件或
正則用符號 | 來表示或,也叫做分支條件,當滿足正則里的分支條件的任何一種條件時,都會當成是匹配成功。
^(130|131|132|155|156|185|186|145|176)\d{8}$
6、區間
正則提供一個元字符中括號 [] 來表示區間條件。
- 限定0到9 可以寫成[0-9]
- 限定A-Z 寫成[A-Z]
- 限定某些數字 [165]
那上面的正則我們還改成這樣
^((13[0-2])|(15[56])|(18[5-6])|145|176)\d{8}$
二、正則進階知識點
1、零寬斷言
語法 | 說明 |
---|---|
(expr) | 捕獲 expr 子模式,以 \1 使用它。 |
(?:expr) | 忽略捕獲的子模式。 |
(?=expr) | 正向預查模式 expr。 |
(?!expr) | 負向預查模式 expr。 |
(?=pattern)
正向肯定預查(look ahead positive assert),匹配pattern前面的位置。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以后使用。
簡單說,以 xxx(?=pattern)為例,就是捕獲以pattern結尾的內容xxx
例如,“Windows(?=95|98|NT|2000)“能匹配"Windows2000"中的"Windows”,但不能匹配"Windows3.1"中的"Windows”。預查不消耗字符,也就是說,在一個匹配發生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預查的字符之后開始。
(?!pattern)
正向否定預查(negative assert),在任何不匹配pattern的字符串開始處匹配查找字符串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以后使用。
簡單說,以 xxx(?!pattern)為例,就是捕獲不以pattern結尾的內容xxx
例如"Windows(?!95|98|NT|2000)“能匹配"Windows3.1"中的"Windows”,但不能匹配"Windows2000"中的"Windows"。預查不消耗字符,也就是說,在一個匹配發生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預查的字符之后開始。
(?<=pattern)
反向(look behind)肯定預查,與正向肯定預查類似,只是方向相反。
簡單說,以(?<=pattern)xxx為例,就是捕獲以pattern開頭的內容xxx。
例如,"(?<=95|98|NT|2000)Windows"能匹配"2000Windows"中的"Windows",但不能匹配"3.1Windows"中的"Windows"。
(?<!pattern)
簡單說,以(?<!pattern)xxx為例,就是捕獲不以pattern開頭的內容xxx。
反向否定預查,與正向否定預查類似,只是方向相反。例如"(?<!95|98|NT|2000)Windows"能匹配"3.1Windows"中的"Windows",但不能匹配"2000Windows"中的"Windows"。
2、零寬斷言測試
class DemoApplicationTests {void contextLoads() {//創建測試樣例String test = "abc123xyz";//創建正則表達式String reg1 = "\\w(?=123)";String reg2 = "\\w(?!123)";String reg3 = "(?<=abc)\\w";String reg4 = "(?<!abc)23";Pattern pattern1 = Pattern.compile(reg1);Pattern pattern2 = Pattern.compile(reg2);Pattern pattern3 = Pattern.compile(reg3);Pattern pattern4 = Pattern.compile(reg4);//查找Matcher mc1 = pattern1.matcher(test);Matcher mc2 = pattern2.matcher(test);Matcher mc3 = pattern3.matcher(test);Matcher mc4 = pattern4.matcher(test);if (mc1.find()) {System.out.println(mc1.group());}if (mc2.find()) {System.out.println(mc2.group());}if (mc3.find()) {System.out.println(mc3.group());}if (mc4.find()) {System.out.println(mc4.group());}}
}
3、捕獲和非捕獲
數字編號捕獲組
語法:(exp)解釋:從表達式左側開始,每出現一個左括號和它對應的右括號之間的內容為一個分組,在分組中,第0組為整個表達式,第一組開始為分組。
比如固定電話的:020-85653333
他的正則表達式為:(0\d{2})-(\d{8})
按照左括號的順序,這個表達式有如下分組:
序號 | 編號 | 分組 | 內容 |
---|---|---|---|
0 | 0 | (0\d{2})-(\d{8}) | 020-85653333 |
1 | 1 | (0\d{2]) | 020 |
2 | 2 | (\d{8}) | 85653333 |
class DemoApplicationTests {@Testvoid contextLoads1() {String test = "020-85653333";String reg = "(0\\d{2})-(\\d{8})";Pattern pattern = Pattern.compile(reg);Matcher mc = pattern.matcher(test);if (mc.find()) {System.out.println("分組的個數有:" + mc.groupCount());for (int i = 0; i <= mc.groupCount(); i++) {System.out.println("第" + i + "個分組為:" + mc.group(i));}}}}
可見,分組個數是2,但是因為第0個為整個表達式本身,因此也一起輸出了。
命名編號捕獲組
語法:(?exp)
解釋:分組的命名由表達式中的name指定
比如區號也可以這樣寫:(?\0\d{2})-(?\d{8})
按照左括號的順序,這個表達式有如下分組:
序號 | 編號 | 分組 | 內容 |
---|---|---|---|
0 | 0 | (0\d{2})-(\d{8}) | 020-85653333 |
1 | quhao | (0\d{2]) | 020 |
2 | haoma | (\d{8}) | 85653333 |
class DemoApplicationTests {@Testvoid contextLoads1() {String test = "020-85653333";String reg = "(?<quhao>0\\d{2})-(?<haoma>\\d{8})";Pattern pattern = Pattern.compile(reg);Matcher mc = pattern.matcher(test);if (mc.find()) {System.out.println("分組的個數有:" + mc.groupCount());System.out.println(mc.group("quhao"));System.out.println(mc.group("haoma"));}}
}
非捕獲組
語法:(?:exp)
(?:expr) 忽略捕獲的子模式。
解釋:和捕獲組剛好相反,它用來標識那些不需要捕獲的分組,說的通俗一點,就是你可以根據需要去保存你的分組。
序號 | 編號 | 分組 | 內容 |
---|---|---|---|
0 | 0 | (0\d{2})-(\d{8}) | 020-85653333 |
1 | quhao | (0\d{2]) | 020 |
比如上面的正則表達式,程序不需要用到第一個分組,那就可以這樣寫:
class DemoApplicationTests {@Testvoid contextLoads1() {String test = "020-85653333";String reg = "(?:0\\d{2})-(\\d{8})";Pattern pattern = Pattern.compile(reg);Matcher mc = pattern.matcher(test);if (mc.find()) {System.out.println("分組的個數有:" + mc.groupCount());for (int i = 0; i <= mc.groupCount(); i++) {System.out.println("第" + i + "個分組為:" + mc.group(i));}}}
}
3、反向引用
根據捕獲組的命名規則,反向引用可分為
- 數字編號組反向引用:\k或\number
- 命名編號組反向引用:\k或者’name’
反向引用如:\1,\2等
\1:表示的是引用第一次匹配到的()括起來的部分
\2:表示的是引用第二次匹配到的()括起來的部分
例:(\d)\1
首先這里是匹配兩位,\d一位,\1又引用\d一位 這里的\1會去引用(\d)匹配到的內容,因為(\d)是第一次匹配到的內容。
如:str = "22"時,(\d)匹配到2,所以\1引用(\d)的值也為2,所以str="22"能匹配
str = "23"時,(\d)匹配到2,因為\1引用(\d)的值2,而這里是3,所以str="23"不能匹配
例:(\d)\10-9\2{2}
這里使用了\2引用第二次匹配到的分組,這里第二次匹配的分組為\2前面的(\d),這里的{2}指的是\2的值出現兩次
如:第一個(\d)為4時,\1引用第1個(\d)也為4,第二個(\d)為5時,\2引用第二個(\d)為5,所以結果可以是:447555,440222
例:(\d)\1[0-9](\d)\1{2}
注意在后面第二個(\d)\1{2}中的\1,這里的\1并不會去匹配他前面的(\d),而是匹配第一個(\d),
如:第一個(\d)為3時,則第一個\1也為3,同樣最后那個\1也為3,所以結果可以是335933,332533而不是336444,339888
例:((\d)3)\1[0-9](\d)\2{2}
當匹配中的分組有嵌套時,是從外向里匹配的,其次在由左向右匹配
這里主要是分析匹配到分組的順序,首先匹配((\d)3)這整個部分,其次匹配((\d)3)里面的(\d),第三次匹配時最后一個\2前面的(\d)
如:如((\d)3)中的(\d)為2時,((\d)3)的值為23,此時\1為((\d)3)的值1,而\2引用((\d)3)中的(\d)的值3,第三個(\d)為5時,此時\3引用第三個(\d)的值5,所以結果可以有:23238522,23230522,
三、貪婪和非貪婪
1、貪婪
貪婪匹配:當正則表達式中包含能接受重復的限定符時,通常的行為是(在使整個表達式能得到匹配的前提下)匹配盡可能多的字符,這匹配方式叫做貪婪匹配。特性:一次性讀入整個字符串進行匹配,每當不匹配就舍棄最右邊一個字符,繼續匹配,依次匹配和舍棄(這種匹配-舍棄的方式也叫做回溯),直到匹配成功或者把整個字符串舍棄完為止,因此它是一種最大化的數據返回,能多不會少。
用來匹配3到6位數字,在這種情況下,它是一種貪婪模式的匹配,也就是假如字符串里有6個個數字可以匹配,那它就是全部匹配到。
如
class DemoApplicationTests {void contextLoads3() {String reg = "\\d{3,6}";String test = "61762828 176 2991 871";System.out.println("文本:" + test);System.out.println("貪婪模式:" + reg);Pattern p1 = Pattern.compile(reg);Matcher m1 = p1.matcher(test);while (m1.find()) {System.out.println("匹配結果:" + m1.group(0));}}
}
是這樣的,多個貪婪在一起時,如果字符串能滿足他們各自最大程度的匹配時,就互不干擾,但如果不能滿足時,會根據深度優先原則,也就是從左到右的每一個貪婪量詞,優先最大數量的滿足,剩余再分配下一個量詞匹配。
class DemoApplicationTests {@Testvoid contextLoads3() {String reg = "(\\d{1,2})(\\d{3,4})";String test = "61762828 176 2991 87321";System.out.println("文本:" + test);System.out.println("貪婪模式:" + reg);Pattern p1 = Pattern.compile(reg);Matcher m1 = p1.matcher(test);while (m1.find()) {System.out.println("匹配結果:" + m1.group(0));}}
}
- “617628” 是前面的\d{1,2}匹配出了61,后面的匹配出了7628
- “2991” 是前面的\d{1,2}匹配出了29 ,后面的匹配出了91
- "87321"是前面的\d{1,2}匹配出了87,后面的匹配出了321
2、懶惰(非貪婪)
懶惰匹配:當正則表達式中包含能接受重復的限定符時,通常的行為是(在使整個表達式能得到匹配的前提下)匹配盡可能少的字符,這匹配方式叫做懶惰匹配。特性:從左到右,從字符串的最左邊開始匹配,每次試圖不讀入字符匹配,匹配成功,則完成匹配,否則讀入一個字符再匹配,依此循環(讀入字符、匹配)直到匹配成功或者把字符串的字符匹配完為止。
代碼 | 說明 |
---|---|
*? | 重復任意次,但盡可能少重復 |
+? | 重復1次或更多次,但盡可能少重復 |
?? | 重復0次或1次,但盡可能少重復 |
{n,m}? | 重復n到m次,但盡可能少重復 |
{n,}? | 重復n次以上,但盡可能少重復 |
class DemoApplicationTests {void contextLoads3() {String reg = "(\\d{1,2}?)(\\d{3,4})";String test = "61762828 176 2991 87321";System.out.println("文本:" + test);System.out.println("貪婪模式:" + reg);Pattern p1 = Pattern.compile(reg);Matcher m1 = p1.matcher(test);while (m1.find()) {System.out.println("匹配結果:" + m1.group(0));}}
}
解答
“61762” 是左邊的懶惰匹配出6,右邊的貪婪匹配出1762
“2991” 是左邊的懶惰匹配出2,右邊的貪婪匹配出991
“87321” 左邊的懶惰匹配出8,右邊的貪婪匹配出7321
四、反義
前面說到元字符的都是要匹配什么什么,當然如果你想反著來,不想匹配某些字符,正則也提供了一些常用的反義元字符:
元字符 | 解釋 |
---|---|
\w | 匹配任意不是字母,數字,下劃線,漢字的字符 |
\s | 匹配任意不是空白符的字符 |
\D | 匹配任意非數字的字符 |
\B | 匹配不是單詞開頭或結束的位置 |
[^x] | 匹配除了x以外的任意亨符 |
[^aeiou] | 匹配除了aeiou這幾個字母以外的任意字符 |
五、 案例
1、匹配時間
\d{4}年?/?\d{1,2}月?/?\d{1,2}日?([ ]\d{1,2}:?時?\d{1,2}:?分?\d{1,2}秒?)?
本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處:https://dhexx.cn/hk/18278.html
如若內容造成侵權/違法違規/事實不符,請聯系我的編程經驗分享網進行投訴反饋,一經查實,立即刪除!