引子
上一章分享了正式表達(dá)式的入門(mén)知識(shí),以及單字符、多字符常用的匹配方法,對(duì)于工作維護(hù)過(guò)程中已經(jīng)夠用,但是有時(shí)候只使用基礎(chǔ)知識(shí)來(lái)實(shí)現(xiàn)就會(huì)比較麻煩,如果使用高級(jí)用法就會(huì)比較方便很多。
例如:匹配一個(gè)HTML文件中兩個(gè)<B>標(biāo)簽中的文件。
文本內(nèi)容:
This offer is not available to customers living in <B>AK</B> and <B>HI</B>
從上一章內(nèi)容的知識(shí)可以想到的表達(dá)式可能如下:
<[Bb]>.*</[Bb]>
但是這個(gè)表達(dá)式配置的結(jié)果是AK</B> and <B>HI,而不是我們想要的AK和HI。
懶惰型匹配
引子中的例子中的匹配方式是屬于貪婪行為,就是盡可能多的匹配內(nèi)容,像例子中第一個(gè)<B>和最后一個(gè)</B>中間都被匹配到了,而不管匹配內(nèi)容中是否存在</B>。
上一章中講到的*和+、{m,}都是所謂的“貪婪型”的。在這一節(jié)中講一下與“貪婪型”相反的“懶惰型”,就是匹配盡可能少的內(nèi)容。
實(shí)現(xiàn)很簡(jiǎn)單,就是在原有“貪婪型”元字符后面加上一個(gè)? 號(hào),如下表格
| 貪婪型元字符 | 懶惰型元字符 |
|---|---|
| * | *? |
| + | +? |
| {m,} | {m,}? |
位置匹配
在現(xiàn)實(shí)的系統(tǒng)中一般表達(dá)位置的地方就是一個(gè)單詞的開(kāi)頭以及結(jié)尾或者一個(gè)字符串的開(kāi)頭或者結(jié)尾。
注意這個(gè)邊界只是一個(gè)位置,例如單詞邊界匹配的是\w和\W之間的一個(gè)位置
| 元字符 | 說(shuō)明 | 注意 |
|---|---|---|
| \b | 單詞邊界,單詞的開(kāi)頭或者單詞的結(jié)尾 | 回退鍵的元字符是[\b]
|
| \B | 表示非單詞邊界 | |
| ^ | 字符串的開(kāi)頭位置 | 放在[]中表示取非操作 |
| $ | 字符串結(jié)尾位置 |
擴(kuò)展:
- 像egrep中也支持使用
\<匹配單詞開(kāi)頭位置,使用\>匹配單詞結(jié)尾位置,但是支持這種元字符的編輯器比較少。 - (?m)是一個(gè)分行匹配模式的記號(hào),放在一個(gè)表達(dá)式的最前面,會(huì)改變字符串位置匹配的行為。
^不僅匹配正常的字符串開(kāi)頭還匹配行分隔符(換行符)后面的開(kāi)始位置;同樣$不僅匹配正常的字符串結(jié)尾還匹配行分隔符(換行符)后面的結(jié)束位置;此用法只有部分正則表達(dá)式會(huì)支持
| 選項(xiàng) | 描述 | 支持平臺(tái) |
|---|---|---|
| (?d) | Unix中的行 | java |
| (?i) | 不區(qū)分大小寫(xiě) | PCRE Perl java |
| (?J) | 允許重復(fù)的名字 | PCRE* |
| (?m) | 多行 | PCRE Perl java |
| (?s) | 單行 | PCRE Perl java |
| (?u) | Unicode | java |
| (?U) | 默認(rèn)最短匹配,與懶惰型匹配類似 | PCRE |
| (?x) | 忽略空格和注釋 | PCRE Perl Java |
| (?-...) | 復(fù)原或關(guān)閉選項(xiàng) | PCRE |
捕獲分組與后向引用
前面的元字符都是對(duì)緊挨著前面的一個(gè)字符有效,例如表達(dá)式the{3}匹配theee字符串,假如我們想匹配連續(xù)三個(gè)the字符串怎么辦呢,這就涉及到子表達(dá)式的概念。
子表達(dá)式
把一個(gè)表達(dá)式匹配的內(nèi)容做為一個(gè)單獨(dú)的元素嵌入到另外一個(gè)表達(dá)式中,那這個(gè)做為獨(dú)立元素的表達(dá)式就是子表達(dá)式,需要使用()括起來(lái)。這個(gè)跟數(shù)學(xué)的表達(dá)式概念很類似。
并且子表達(dá)與數(shù)學(xué)表達(dá)式還有一個(gè)類似的地方就是,正則表達(dá)式的子表達(dá)式也可以嵌套使用
本節(jié)開(kāi)頭說(shuō)的那個(gè)問(wèn)題就可以使用子表達(dá)式來(lái)實(shí)現(xiàn),
(the){3}就會(huì)匹配thethethe這個(gè)字符串。
假如我們?cè)偌觽€(gè)條件:我們想匹配連續(xù)三個(gè)the或者連續(xù)三個(gè)you,怎么實(shí)現(xiàn)?這就是正則表達(dá)式的選擇操作符,也叫或操作符了
| 元字符 | 說(shuō)明 |
|---|---|
| |
或操作符,兩邊的表達(dá)式都是一個(gè)獨(dú)立的元素,一般放在()中使用 |
上面的問(wèn)題就可以使用正則表達(dá)式(the|you){3}來(lái)表示
捕獲分組與后向引用
當(dāng)一個(gè)模式的全部或者部分內(nèi)容由一對(duì)括號(hào)括起來(lái)時(shí),就對(duì)表達(dá)式進(jìn)行了分組(其實(shí)就是放在()中的子表達(dá)式),并且把分組匹配到內(nèi)容捕獲并且臨時(shí)存放在內(nèi)存中。這就是捕獲分組,可以在后面表達(dá)式中使用就叫后向引用,或者叫回溯引用。
默認(rèn)情況下,分組是從左到右依次排序從1編號(hào),第一個(gè)分組就是1,第二個(gè)分組就是2等等。
最開(kāi)始的時(shí)候支持的編號(hào)范圍是1到9,現(xiàn)在應(yīng)該已經(jīng)沒(méi)有這種限制了。
后向引用很簡(jiǎn)單就是一個(gè)\或者$后面跟相應(yīng)編號(hào)即可。例如\1或者$1就表示引用第一個(gè)捕獲分組。
命名分組
前面講捕獲分組都是通過(guò)位置編號(hào)來(lái)訪問(wèn),在perl和python、.NET等語(yǔ)言中還支持對(duì)捕獲分組命名。這樣就比較容易理解
| 命名語(yǔ)法 | 描述 |
|---|---|
| (?<name>分組) | 命名分組 |
| (?P<name>分組) | python中的命名分組 |
| \k<name> | Perl中引用命名分組 |
| \k'name' | Perl中引用命名分組 |
| \g{name} | Perl中引用命名分組 |
| \k{name} | .NET中引用命名分組 |
| (?P=name) | Python中引用命名分組 |
非捕獲分組
顧名思義,與捕獲分組相反,就是不會(huì)將分組匹配的內(nèi)容放在內(nèi)存中。主要是為了提高性能。
使用方法:在分組的開(kāi)頭加上?:,例如(?:the)
當(dāng)把非捕獲分組語(yǔ)法中的
:換成>時(shí),就變成了原子分組(另一種非捕獲分組),可以進(jìn)一步提升性能。因?yàn)樵臃纸M會(huì)將分組內(nèi)部的回溯操作關(guān)閉。
環(huán)視
環(huán)視是一種非捕獲分組,它根據(jù)某個(gè)模式之前或者之后的內(nèi)容要求匹配其他模式。環(huán)視也稱為零寬度斷言。
| 環(huán)視分類 | 說(shuō)明 | 舉例 |
|---|---|---|
| (?=分組) | 正前瞻,匹配且要求緊隨其后內(nèi)容為分組匹配的內(nèi)容 |
a(?=b),匹配a并且后面堅(jiān)接著是b的字符串,可以匹配abc但是不匹配acb
|
| (?!分組) | 反前瞻,即對(duì)正前瞻含義取反,匹配且要求緊隨其后內(nèi)容不為分組匹配的內(nèi)容 |
a(?!b),匹配a并且后面堅(jiān)接著不是b的字符串,可以匹配acb但是不匹配abc
|
| (?<=分組) | 正后顧,即對(duì)正前瞻方向取反,匹配且要求緊挨著之前的內(nèi)容為分組匹配的內(nèi)容 |
(?<=a)b),匹配b并且前面緊挨著是a的字符串,可以匹配abc但是不匹配cbc
|
| (?<!分組) | 反后顧,即對(duì)正后顧含義取反,匹配且要求緊挨著之前的內(nèi)容不為分組匹配的內(nèi)容 |
(?<!a)b),匹配b并且前面緊挨著不是a的字符串,可以匹配cbc但是不匹配abc
|
條件匹配
(?(id/name)yes-pattern|no-pattern)
如果給定的 id 或 name 存在,將會(huì)嘗試匹配yes-pattern;否則就嘗試匹配no-pattern,no-pattern可選;
例如:email樣式匹配(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$),當(dāng)<存在時(shí),則最后要匹配>;否則匹配結(jié)束符$
參考
《學(xué)習(xí)正則表達(dá)式》
《正則表達(dá)式必知必會(huì)》