正則表達式分組()、不捕獲(?:)和斷言(?<=)詳解


分組

分組在正則中用()表示,根據小菜理解,分組的作用有兩個:

1.將某些規律看成是一組,然后進行組級別的重復,可以得到意想不到的效果。

2.分組之后,可以通過后向引用簡化表達式(\1 或者$1)。

分組舉列

先來看第一個作用,對於IP地址的匹配,簡單的可以寫為如下形式:

 \d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}

但仔細觀察,我們可以發現一定的規律,可以把.\d{1,3}看成一個整體,也就是把他們看成一組,再把這個組重復3次即可。表達式如下:

 \d{1,3}(.\d{1,3}){3}

 再來看第二個作用,就拿匹配<title>xxx</title>標簽來說,簡單的正則可以這樣寫:

<title>.*</title>

可以看出,上邊表達式中有兩個title,完全一樣,其實可以通過分組簡寫。表達式如下:

<(title)>.*</\1>

對於分組而言,整個表達式永遠算作第0組,在本例中,第0組是<(title)>.*</\1>,然后從左到右,依次為分組編號,因此,(title)是第1組。

注意:

用\1這種語法,可以引用某組的文本內容,但不能引用正則表達式。

例如剛剛的IP地址正則表達式為\d{1,3}(.\d{1,3}){3},里邊的\d{1,3}重復了兩次,如果利用后向引用簡化,表達式如下:

 (\d{1,3})(.\1){3}

經過實際測試,會發現這樣寫是錯誤的,為什么呢?

后向引用,引用的僅僅是文本內容,而不是正則表達式!

也就是說,組中的內容一旦匹配成功,后向引用,引用的就是匹配成功后的內容,引用的是結果,而不是表達式。

因此,(\d{1,3})(.\1){3}這個表達式實際上匹配的是四個數都相同的IP地址,比如:123.123.123.123。

 

不捕獲

不捕獲就是在分組的前邊加上?:,可以在不需要捕獲分組的表達式中使用,加快表達式執行速度。

就拿匹配<title>xxx</title>標簽來說,通過分組可以簡寫為

<(title)>.*</\1> 

但是如果是(?:title),則\1就不能捕獲到這個子組了,只能捕獲第一個出現的非?:的分組作為\1

同時注意(?:title)本身會在完整匹配中,只是不在子組中,注意和斷言的區別

$str="ab123ff";
//正常,完整匹配為ab123ff, 有兩個子組ab, ff
preg_match_all("|([a-z]{2}).*([a-z]{2})|U", $str, $out);echo "<pre>";
print_r($out);
echo "</pre>";

//不捕獲分組,完整匹配為ab23ff, 有一個子組ff
preg_match_all("|(?:[a-z]{2}).*([a-z]{2})|U", $str, $out);echo "<pre>";
print_r($out);
echo "</pre>";

//斷言,完整匹配為123ff, 有一個子組ff
preg_match_all("|(?<=[a-z]{2}).*([a-z]{2})|U", $str, $out);echo "<pre>";
print_r($out);
echo "</pre>";

 

斷言

所謂斷言,就是指明某個字符串前邊或者后邊,將會出現滿足某種規律的字符串。

就拿匹配<title>xxx</title>標簽來說,我們想要的是xxx,它沒有規律,但是它前邊肯定會有<title>,后邊肯定會有</title>,這就足夠了。

想指定xxx前肯定會出現<title>,就用正后發斷言,表達式:(?<=<title>).*

想指定xxx后邊肯定會出現</title>,就用正先行斷言,表達式:.*(?=</title>)

兩個加在一起,就是(?<=<title>).*(?=</title>)

這樣就能匹配到xxx,匹配的內容不包括斷言的內容,即完整的匹配就是xxx,不會包含<title>和</title>,子組中也不包含

對正后發和正先行的解釋:

其實掌握了規律,就很簡單了,無論是先行還是后發,都是相對於xxx而言的,也就是相對於目標字符串而言。

假如目標字符串后邊有條件,可以理解為目標字符串在前,就用先行斷言,放在目標字符串之后。

假如目標字符串前邊有條件,可以理解為目標字符串在后,就用后發斷言,放在目標字符串之前。

假如指定滿足某個條件,就是正。

假如指定不滿足某個條件,就是負。

斷言只是條件,幫你找到真正需要的字符串,本身並不會匹配!

!表示正好相反的意思,就是把=換成了!,看表格解釋,X代表字符

(?=X )
零寬度正先行斷言。僅當子表達式 X 在 此位置的右側匹配時才繼續匹配。例如,\w+(?=\d) 與后跟數字的單詞匹配,而不與該數字匹配。此構造不會回溯。
(?!X)
零寬度負先行斷言。僅當子表達式 X 不在 此位置的右側匹配時才繼續匹配。例如,例如,\w+(?!\d) 與后不跟數字的單詞匹配,而不與該數字匹配 。
(?<=X)
零寬度正后發斷言。僅當子表達式 X 在 此位置的左側匹配時才繼續匹配。例如,(?<=19)99 與跟在 19 后面的 99 的實例匹配。此構造不會回溯。
(?<!X)
零寬度負后發斷言。僅當子表達式 X 不在此位置的左側匹配時才繼續匹配。例如,(?<!19)99 與不跟在 19 后面的 99 的實例匹配
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM