這次不會說我的正則教程沒寫全了吧??
來自專欄編程無界1 人贊了文章
由於上一篇文章:《正則表達式真的很騷,可惜你不會寫!!!》 發表之後,不少網友說怎麼沒講斷言沒講反向沒講貪婪….,甚至有老鐵說我褲子都脫了你就給講了一點,哈哈哈,好吧,趁著山竹颱風被迫放假在家的時間,把正則剩餘的一些知識點給講一下,希望大家喜歡,希望這次脫褲子閱讀的老鐵可以暢快的操作了。
本文旨在用最通俗的語言講述最枯燥的基本知識。
關注我的公眾號(ID:編程無界)有更多精彩內容哦,而且不定期送書活動。
文章提綱:
- 零寬斷言
- 捕獲和非捕獲
- 反向引用
- 貪婪和非貪婪
- 反義
1. 零寬斷言
無論是零寬還是斷言,聽起來都古古怪怪的,
那先解釋一下這兩個詞。
- 斷言:俗話的斷言就是「我斷定什麼什麼」,而正則中的斷言,就是說正則可以指明在指定的內容的前面或後面會出現滿足指定規則的內容,意思正則也可以像人類那樣斷定什麼什麼,比如"ss1aa2bb3",正則可以用斷言找出aa2前面有bb3,也可以找出aa2後面有ss1.
- 零寬:就是沒有寬度,在正則中,斷言只是匹配位置,不佔字元,也就是說,匹配結果里是不會返回斷言本身。
意思是講明白了,那他有什麼用呢?
我們來舉個栗子:假設我們要用爬蟲抓取csdn里的文章閱讀量。通過查看源代碼可以看到文章閱讀量這個內容是這樣的結構1"<span class="read-count">閱讀數:641</span>"
其中只有『641』這個是一個變數,也就是不同文章有不同的值,當我們拿到這個字元串時,需要獲得這裡邊的『641』有很多種辦法,但如果使用正則應該怎麼匹配呢?
下面先講一下幾種類型的斷言:
- 正向先行斷言(正前瞻):
- 語法:(?=pattern)
- 作用:匹配pattern表達式的前面內容,不返回本身。
這樣子說,還是一臉懵逼,好吧,回歸剛才那個栗子,要取到閱讀量,在正則表達式中就意味著要能匹配到『</span>』前面的數字內容
按照上所說的正向先行斷言可以匹配表達式前面的內容,那意思就是:(?=</span>) 就可以匹配到前面的內容了。匹配什麼內容呢?如果要所有內容那就是: 1String reg=".+(?=</span>)"; 2 3String test = "<span class="read-count">閱讀數:641</span>"; 4Pattern pattern = Pattern.compile(reg); 5Matcher mc= pattern.matcher(test); 6while(mc.find()){ 7 System.out.println("匹配結果:") 8 System.out.println(mc.group()); 9}1011//匹配結果:12//<span class="read-count">閱讀數:641
可是老哥我們要的只是前面的數字呀,那也簡單咯,匹配數字 d,那可以改成:
1String reg="\d+(?=</span>)";2String test = "<span class="read-count">閱讀數:641</span>";3Pattern pattern = Pattern.compile(reg);4Matcher mc= pattern.matcher(test);5while(mc.find()){6 System.out.println(mc.group());7}8//匹配結果:9//641
大功告成!
- 正向後行斷言(正後顧):
- 語法:(?<=pattern)
- 作用:匹配pattern表達式的後面的內容,不返回本身。
有先行就有後行,先行是匹配前面的內容,那後行就是匹配後面的內容啦。
上面的栗子,我們也可以用後行斷言來處理. 1//(?<=<span class="read-count">閱讀數:)d+ 2String reg="(?<=<span class="read-count">閱讀數:)\d+"; 3 4String test = "<span class="read-count">閱讀數:641</span>"; 5Pattern pattern = Pattern.compile(reg); 6Matcher mc= pattern.matcher(test); 7 while(mc.find()){ 8 System.out.println(mc.group()); 9 }10//匹配結果:11//641
就這麼簡單。
- 負向先行斷言(負前瞻)
- 語法:(?!pattern)
- 作用:匹配非pattern表達式的前面內容,不返回本身。
有正向也有負向,負向在這裡其實就是非的意思。
舉個栗子:比如有一句 「我愛祖國,我是祖國的花朵」現在要找到不是的花朵前面的祖國用正則就可以這樣寫:1祖國(?!的花朵)
- 負向後行斷言(負後顧)
- 語法:(?<!pattern)
- 作用:匹配非pattern表達式的後面內容,不返回本身。
2. 捕獲和非捕獲
單純說到捕獲,他的意思是匹配表達式,但捕獲通常和分組聯繫在一起,也就是「捕獲組」
捕獲組:匹配子表達式的內容,把匹配結果保存到內存中中數字編號或顯示命名的組裡,以深度優先進行編號,之後可以通過序號或名稱來使用這些匹配結果。
而根據命名方式的不同,又可以分為兩種組:
- 數字編號捕獲組:語法:(exp)解釋:從表達式左側開始,每出現一個左括弧和它對應的右括弧之間的內容為一個分組,在分組中,第0組為整個表達式,第一組開始為分組。比如固定電話的:020-85653333他的正則表達式為:(0d{2})-(d{8})按照左括弧的順序,這個表達式有如下分組:
序號編號分組內容00(0d{2})-(d{8})020-8565333311(0d{2})02022(d{8})85653333
我們用Java來驗證一下:
1String test = "020-85653333"; 2 String reg="(0\d{2})-(\d{8})"; 3 Pattern pattern = Pattern.compile(reg); 4 Matcher mc= pattern.matcher(test); 5 if(mc.find()){ 6 System.out.println("分組的個數有:"+mc.groupCount()); 7 for(int i=0;i<=mc.groupCount();i++){ 8 System.out.println("第"+i+"個分組為:"+mc.group(i)); 9 }10 }
輸出結果:
1分組的個數有:22第0個分組為:020-856533333第1個分組為:0204第2個分組為:85653333
可見,分組個數是2,但是因為第0個為整個表達式本身,因此也一起輸出了。
- 命名編號捕獲組:語法:(?<name>exp)解釋:分組的命名由表達式中的name指定比如區號也可以這樣寫:(?<quhao>