茫茫人海中,只找到了你
1. 什么是正則表達(dá)式
在某些情況下,我們總是要在某個文本中查找某些復(fù)雜規(guī)則的字符串,正則表達(dá)式就是用戶描述這些規(guī)則的工具(正則表達(dá)式就是記錄文本規(guī)則的代碼)。
2. 舉個簡單的栗子
假設(shè)在一篇英文文章中,你要查找hi這個單詞,這時候查找到的不僅有hi這個單詞,還有類似于hidden、hight等等的,其實這不是我們想要的,我們要的僅僅就是hi這個詞,而這個時候,我們就應(yīng)該使用\bhi\b
元字符
\b:是正則表達(dá)式規(guī)定的一個特殊代碼,代表著單詞的開頭或結(jié)尾,即單詞的分界處。需要說明的是,\b并不匹配單詞分割字符(空格、標(biāo)點符號、換行等),它只匹配一個位置。元字符
.:用于匹配除了換行符以外的任意字符。元字符
*:*既不代表字符,也不是位置,而是數(shù)量;它指定前邊的內(nèi)容可以連續(xù)重復(fù)使用任意次以使得整個表達(dá)式得到匹配。
回到上面的例子,如果更換需求,現(xiàn)在需要查找的是hi###Max,###代表的是任意字符,這時候我們就可以使用上面的三個元字符組合來查找這個特定規(guī)則的字符串了,即: \bhi\b.*\bMax\b。
目前我們只知道三個元字符,如果我們知道了更多的元字符,我們就可以構(gòu)造出更強(qiáng)大的正則表達(dá)式。
1. 元字符
從以上的例子中我們已經(jīng)知道了三個元字符了,下面我就來給大家再通過栗子介紹幾個常用的元字符。
| 代碼 | 說明 |
|---|---|
\w |
匹配字母或者數(shù)字或者下劃線或者漢字 |
\s |
匹配任意的空白符(包括空格、制表符、換行符、中文全角空格等) |
\d |
匹配數(shù)字 |
\b |
匹配單詞的開始或者結(jié)束 |
^ |
匹配字符串的開始 |
$ |
匹配字符串的結(jié)束 |
\ba\w*\b:匹配以字母a開頭的單詞(顯示以單詞開始處\b,然后是字母a,然后是任意數(shù)量的字母或者數(shù)字\w,最后是單詞結(jié)尾處\b)\d+:匹配1個或者更多連續(xù)的數(shù)字。這里的+是和*類似的元字符,不同的是*匹配重復(fù)任意次(可能是0次),而+則匹配重復(fù)1次或者更多次。0\d{2}-\d{8}:匹配以0開頭,然后是兩個數(shù)字,然后是一個連字號-,最后是8個數(shù)字\b\w{6}\b:匹配剛好6個字符的單詞^\d{5,12}$:匹配的是5位到12位數(shù)字(因為使用了^和$,所以輸入的整個字符串都要用來和\d{5,12}來匹配,即整個輸入的必須是5到12個數(shù)字)
2. 字符轉(zhuǎn)義
如果我想在文本中查找元字符本身的話,該怎么查找呢?這時候就要使用轉(zhuǎn)義了
例如你要查找.,就應(yīng)該使用\.。如果你要查找*,就應(yīng)該使用\*。如果你要查找\,就應(yīng)該使用\\
3. 重復(fù)
| 代碼 | 說明 |
|---|---|
* |
重復(fù)零次或者更多次 |
+ |
重復(fù)一次或者更多次 |
? |
重復(fù)零次或者一次 |
{n} |
重復(fù)n次 |
{n,} |
重復(fù)n次或者更多次 |
{n,m} |
重復(fù)n到m次 |
4. 字符類
\d:代表的是0-9的數(shù)字集合,怎么可以自定義字符集呢,可以使用[]來實現(xiàn)
[0-9]代表的意義與\d就是完全一樣的[a-z0-9A-Z]也完全等同于\w(不考慮漢字)\(?0\d{2}[) -]?\d{8}:這個就稍微復(fù)雜了,這個表達(dá)式可以匹配下面幾種格式的電話號碼,如:(010)66668888、022-11335577、03312378956分析一下:首先是一個轉(zhuǎn)義字符\(,后面跟了一個?,代表這個(可以出現(xiàn)一次或者零次,其次是0,后面跟著2位數(shù)字\d{2},再后面是一個字符集分別是)、、-,后面有個?,代表這個字符集中的字符可以出現(xiàn)零次或者一次,再之后就是8位數(shù)字咯\d{8}。
5. 分枝條件
正則表達(dá)式里的分枝條件是指有幾種規(guī)則,如果滿足其中任意一種規(guī)則都應(yīng)該匹配,具體方法是使用|把不同的規(guī)則分隔開(分枝條件會從左到右測試執(zhí)行每個條件)。
??
0\d{2}-\d{8}|0\d{3}-\d{7}:這個表達(dá)式能夠匹配到兩種以連字符號分隔的電話號碼:一種是三位區(qū)號、8位本地號碼(如:101-88886666),一種是4位區(qū)號、7位本地號碼(如:0371-6677889)\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}:這個表達(dá)式匹配3位區(qū)號的電話號碼,其中區(qū)號可以用小括號括起來,也可以不用,區(qū)號與本地號之間可以用連字號或者空格間隔,也可以沒有間隔。
6. 分組
分組的主要目的就是重復(fù)。上面我們已經(jīng)知道了怎么重復(fù)單個字符(直接在字符后面加上限定字符就可以了);但如果想要重復(fù)多個字符改怎么辦呢? 這就用到了分組:使用小括號來指定子表達(dá)式然后就可以指定這個子表達(dá)式的重復(fù)次數(shù)了。
-
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):這個表達(dá)式匹配的是IP地址。下面就拆分開來逐個分析咯
從左到右
2[0-4]\d:此表達(dá)式主要是匹配200-249的數(shù)字25[0-5]:此表達(dá)式主要是匹配250-255的數(shù)字[01]?\d\d?: 此表達(dá)式主要是匹配0-199的數(shù)字(2[0-4]\d|25[0-5]|[01]?\d\d?)\.: 此表達(dá)式為匹配到0-255的數(shù)字,并在后面跟一個.((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}:此表達(dá)式為將上一步的表達(dá)式重復(fù)3次
至于后面的跟前面的一樣,就不分析了
7. 反義
| 代碼 | 說明 |
|---|---|
\W |
匹配任意不是字母、數(shù)字、下劃線、漢字的字符 |
\S |
匹配任意不是空白符的字符 |
\D |
匹配任意非數(shù)字的字符 |
\B |
匹配不是單詞開頭或者結(jié)束的位置 |
[^x] |
匹配除了x以外的任意字符 |
[^aeiou] |
匹配除了aeiou這幾個字母以外的任意字符 |
8. 向后引用
使用小括號指定一個子表達(dá)式后,匹配這個子表達(dá)式的文本可以在表達(dá)式或者其它程序中作進(jìn)一步的處理。默認(rèn)情況下,每個分組都會自動擁有一個組號,規(guī)則是:從左向右,以分組的左括號為標(biāo)志,第一個出現(xiàn)的分組的組號為1,第二個為2,以此類推。
向后引用用于重復(fù)搜索前面某個分組匹配的文本。如:\1,表示分組1匹配的文本
-
\b(\w+)\b\s+\1\b:可以用來匹配重復(fù)的單詞,如:go go或者kitty kitty。這個表達(dá)式首先是一個單詞,也就是單詞的開始處和結(jié)束處之間的多于一個的字母或者數(shù)字(\b(\w+)\b),這個單詞會被普貨到編號為1的分組中,然后是1個或幾個空白符(\s+),最后是分組1中捕獲的內(nèi)容(\1)
當(dāng)然也可以指定子表達(dá)式的組名。
要指定一個表達(dá)式的組名,需要使用這樣的語法:(?<Name>\w+)(或者把尖括號換成',即(?'Name'\w+)),這樣就把\w+的組名指定為Name了。要反向引用這個分組捕獲的內(nèi)容,可以使用\k<Name>,所以上一個栗子也可以寫成這樣紙:\b(?<Name>\w+)\b\s+\k<Name>\b
下表列出了小括號的一些常用的語法
| 分類 | 代碼 | 說明 |
|---|---|---|
| 捕獲 | (exp) |
匹配exp,并捕獲文本到自動命名的組里 |
| 捕獲 | (?<name>exp) |
匹配exp,并捕獲文本到名稱為name的組里,也可以寫成(?'name'exp)
|
| 捕獲 | (?:exp) |
匹配exp,不捕獲匹配的文本,也不給此分組分配組號 |
| 零寬斷言 | (?=exp) |
匹配exp前面的位置 |
| 零寬斷言 | (?<=exp) |
匹配exp后面的位置 |
| 零寬斷言 | (?!exp) |
匹配后面跟的不是exp的位置 |
| 零寬斷言 | (?<!exp) |
匹配前面不是exp的位置 |
| 注釋 | (?#comment) |
這種類型的分組不對正則表達(dá)式的處理產(chǎn)生任何影響,用于提供注釋讓人閱讀 |
我們已經(jīng)討論了前兩種語法。第三個(?:exp)不會改變正則表達(dá)式的處理方式,只是這樣的組匹配的內(nèi)容不會像前兩種那樣被捕獲到某個組里面,也不會擁有組號。
9. 零寬斷言
上表中的四個零寬斷言用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之前或者之后的東西,也就是說它們像\b、^、$那樣用于指定一個位置,這個位置應(yīng)該滿足一定的條件(即斷言),因此它們也被稱為零寬斷言。
??
(?=exp)也叫零寬度正預(yù)測先行斷言,它斷言自身出現(xiàn)的位置的后面能匹配表達(dá)式exp。如:\b\w+(?=ing\b),匹配以ing結(jié)尾的單詞的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.時,它會匹配sing和danc。
(?<=exp)也叫零寬度正回顧后發(fā)斷言,它斷言自身出現(xiàn)的位置的前面能匹配表達(dá)式exp。如:(?<=\bre)\w+\b會匹配以re開頭的單詞的后半部分(除了re以外的部分),如查找reading a book時,它匹配ading。
(?<=\s)\d+(?=\s)匹配以空白符間隔的數(shù)字(不包括這些空白符)。
10. 負(fù)向零寬斷言
(?!exp)也叫零寬度負(fù)預(yù)測先行斷言,它斷言此位置的后面不能匹配表達(dá)式exp。
(?<!exp)也叫零寬度負(fù)回顧后發(fā)斷言,它**斷言此位置的前面不能匹配表達(dá)式exp。
??
-
\d{3}(?!\d):匹配三位數(shù)字,并且這三位數(shù)字后面不能是數(shù)字 -
\b((?!abc)\w)+\b:匹配不包含連續(xù)字符串abc的單詞 -
(?<![a-z])\d{7}:匹配前面不是小寫字母的七位數(shù)字 -
(?<=<(\w+)>).*(?=<\/\1>):匹配不包含屬性的簡單HTML標(biāo)簽里的內(nèi)容。分析:(?<=<(\w+)>)指定了這樣的前綴:被尖括號括起來的單詞(比如可能是<b>),然后是.*:任意的字符串,最后是一個后綴(?=<\/\1>),注意后綴中的\/,它是前面介紹過的字符轉(zhuǎn)義;\1則是一個反向引用,引用的正式捕獲的第一組,前面的(\w+)匹配的內(nèi)容,這樣前綴實際上是<b>的話,后綴就是</b>了
11. 注釋
小括號的另一種用途是通過語法(?#comment)來包含注釋。如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。
12. 貪婪與懶惰
a.*b:將會匹配最長的以a開始,以b結(jié)束的字符串。如果用此表達(dá)式搜索匹配aabab,它會匹配整個字符串aabab。這被稱為貪婪匹配。
有時,我們更需要懶惰匹配,也就是匹配盡可能少的字符。以上個例子為準(zhǔn),我們只需要在其后面加上一個問號?,這樣,.*?就意味著匹配任意數(shù)量的重復(fù)、但是在能使整個匹配成功的前提下使用最少的重復(fù)。
-
a.*?b:匹配最短的,以a開始,以b結(jié)束的字符串。如果應(yīng)用于aabab的話,它會匹配aab(第1-3個字符)和ab(第4-5個字符)。
注意:為什么匹配到的第一個是aab(1-3)而不是ab(2-3)。簡單說,正則表達(dá)式有一條規(guī)則比懶惰/貪婪規(guī)則的優(yōu)先級更高:最先開始的匹配擁有最高的優(yōu)先權(quán)
下表為懶惰限定符
| 代碼 | 說明 |
|---|---|
*? |
重復(fù)任意次,但盡可能少重復(fù) |
+? |
重復(fù)1次或者更多次,但盡可能少重復(fù) |
?? |
重復(fù)0次或者1次,但盡可能少重復(fù) |
{n,m}? |
重復(fù)n到m次,但盡可能少重復(fù) |
{n,}? |
重復(fù)n次以上,但盡可能少重復(fù) |
以上這些關(guān)于正則表達(dá)式的介紹已經(jīng)足以讓我們編寫、改寫、讀懂一些常見的正則表達(dá)式,希望以上的介紹能夠幫助到你。
3. 關(guān)于正則表達(dá)式的學(xué)習(xí)
正則表達(dá)式的學(xué)習(xí)最重要的就是栗子,我們可以從不同的案例切入,理解案例并對其修改、實驗(僅代表個人觀點,有更好的觀點可以和大家一起分享)。
關(guān)于測試:網(wǎng)上可以找到一些在線正則表達(dá)式測試的,童鞋們可以在那里測試驗證自己寫的一些表達(dá)式。