摘要:負向先行斷言負前瞻語法作用匹配非表達式的前面內容,不返回本身。我們來測試一下輸出結果嗯,這就是我們想要的了。
由于上一篇文章:《正則表達式真的很騷,可惜你不會寫?。?!》 發表之后,不少網友說怎么沒講斷言沒講反向沒講貪婪....,甚至有老鐵說我褲子都脫了你就給講了一點,哈哈哈,好吧,趁著山竹臺風被迫放假在家的時間,把正則剩余的一些知識點給講一下,希望大家喜歡,希望這次脫褲子閱讀的老鐵可以暢快的操作了。
本文旨在用最通俗的語言講述最枯燥的基本知識。
1. 零寬斷言文章提綱:
零寬斷言
捕獲和非捕獲
反向引用
貪婪和非貪婪
反義
無論是零寬還是斷言,聽起來都古古怪怪的,
那先解釋一下這兩個詞。
斷言:俗話的斷言就是“我斷定什么什么”,而正則中的斷言,就是說正則可以指明在指定的內容的前面或后面會出現滿足指定規則的內容,意思正則也可以像人類那樣斷定什么什么,比如"ss1aa2bb3",正則可以用斷言找出aa2前面有bb3,也可以找出aa2后面有ss1.
零寬:就是沒有寬度,在正則中,斷言只是匹配位置,不占字符,也就是說,匹配結果里是不會返回斷言本身。
意思是講明白了,那他有什么用呢?
我們來舉個栗子:
假設我們要用爬蟲抓取csdn里的文章閱讀量。通過查看源代碼可以看到文章閱讀量這個內容是這樣的結構
"閱讀數:641"
其中只有‘641’這個是一個變量,也就是不同文章有不同的值,當我們拿到這個字符串時,需要獲得這里邊的‘641’有很多種辦法,但如果使用正則應該怎么匹配呢?
下面先講一下幾種類型的斷言:
正向先行斷言(正前瞻):
語法:(?=pattern)
作用:匹配pattern表達式的前面內容,不返回本身。
這樣子說,還是一臉懵逼,好吧,回歸剛才那個栗子,要取到閱讀量,在正則表達式中就意味著要能匹配到‘’前面的數字內容
按照上所說的正向先行斷言可以匹配表達式前面的內容,那意思就是:(?=) 就可以匹配到前面的內容了。
匹配什么內容呢?如果要所有內容那就是:
String reg=".+(?=)"; String test = "閱讀數:641"; Pattern pattern = Pattern.compile(reg); Matcher mc= pattern.matcher(test); while(mc.find()){ System.out.println("匹配結果:") System.out.println(mc.group()); } //匹配結果: //閱讀數:641
可是老哥我們要的只是前面的數字呀,那也簡單咯,匹配數字 d,那可以改成:
String reg="d+(?=)";
String test = "閱讀數:641";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group());
}
//匹配結果:
//641
大功告成!
正向后行斷言(正后顧):
語法:(?<=pattern)
作用:匹配pattern表達式的后面的內容,不返回本身。
有先行就有后行,先行是匹配前面的內容,那后行就是匹配后面的內容啦。
上面的栗子,我們也可以用后行斷言來處理.
//(?<=閱讀數:)d+
String reg="(?<=閱讀數:)d+";
String test = "閱讀數:641";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group());
}
//匹配結果:
//641
就這么簡單。
負向先行斷言(負前瞻)
語法:(?!pattern)
作用:匹配非pattern表達式的前面內容,不返回本身。
有正向也有負向,負向在這里其實就是非的意思。
舉個栗子:比如有一句 “我愛祖國,我是祖國的花朵”
現在要找到不是"的花朵"前面的祖國
用正則就可以這樣寫:
祖國(?!的花朵)
負向后行斷言(負后顧)
語法:(?
作用:匹配非pattern表達式的后面內容,不返回本身。
2. 捕獲和非捕獲單純說到捕獲,他的意思是匹配表達式,但捕獲通常和分組聯系在一起,也就是“捕獲組”
捕獲組:匹配子表達式的內容,把匹配結果保存到內存中中數字編號或顯示命名的組里,以深度優先進行編號,之后可以通過序號或名稱來使用這些匹配結果。
而根據命名方式的不同,又可以分為兩種組:
數字編號捕獲組:
語法:(exp)
解釋:從表達式左側開始,每出現一個左括號和它對應的右括號之間的內容為一個分組,在分組中,第0組為整個表達式,第一組開始為分組。
比如固定電話的:020-85653333
他的正則表達式為:(0d{2})-(d{8})
按照左括號的順序,這個表達式有如下分組:
序號 | 編號 | 分組 | 內容 |
---|---|---|---|
0 | 0 | (0d{2})-(d{8}) | 020-85653333 |
1 | 1 | (0d{2}) | 020 |
2 | 2 | (d{8}) | 85653333 |
我們用Java來驗證一下:
String test = "020-85653333"; String reg="(0d{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個分組為:020-85653333 第1個分組為:020 第2個分組為:85653333
可見,分組個數是2,但是因為第0個為整個表達式本身,因此也一起輸出了。
命名編號捕獲組:
語法:(?
exp)
解釋:分組的命名由表達式中的name指定
比如區號也可以這樣寫:(?0d{2})-(? d{8})
按照左括號的順序,這個表達式有如下分組:
序號 | 名稱 | 分組 | 內容 |
---|---|---|---|
0 | 0 | (0d{2})-(d{8}) | 020-85653333 |
1 | quhao | (0d{2}) | 020 |
2 | haoma | (d{8}) | 85653333 |
用代碼來驗證一下:
String test = "020-85653333"; String reg="(?0d{2})-(? 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")); }
輸出結果:
分組的個數有:2 分組名稱為:quhao,匹配內容為:020 分組名稱為:haoma,匹配內容為:85653333
非捕獲組:
語法:(?:exp)
解釋:和捕獲組剛好相反,它用來標識那些不需要捕獲的分組,說的通俗一點,就是你可以根據需要去保存你的分組。
比如上面的正則表達式,程序不需要用到第一個分組,那就可以這樣寫:
(?: