分組
分組在正則中用()表示,根據小菜理解,分組的作用有兩個:
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 的實例匹配 |