今天下午“光榮”的分到了59個(gè)bug,磨刀不誤砍柴工,先學(xué)一下正則表達(dá)式……
入門(mén)
\b 是正則表達(dá)式規(guī)定的一個(gè)特殊代碼(好吧,某些人叫它 元字符,metacharacter ),代表著 單詞的開(kāi)頭或結(jié)尾,也就是單詞的分界處 。雖然通常英文的單詞是由空格,標(biāo)點(diǎn)符號(hào)或者換行來(lái)分隔的,但是 \b 并不匹配這些單詞分隔字符中的任何一個(gè),它 只匹配一個(gè)位置 。
如果需要更精確的說(shuō)法, \b 匹配這樣的位置:它的前一個(gè)字符和后一個(gè)字符不全是(一個(gè)是,一個(gè)不是或不存在) \w 。
假如你要找的是 hi后面不遠(yuǎn)處跟著一個(gè)Lucy ,你應(yīng)該用 \bhi\b.*\bLucy\b 。若要匹配"A B",則使用 A\sB 或者 A\b\s\bB 等。 表達(dá)式 " . \b . " 在匹配 "@@@abc" 時(shí) ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"@a";匹配到的位置是:開(kāi)始于2,結(jié)束于4。 表達(dá)式 " \b end \b " 在匹配 "weekend,endfor,end" 時(shí) ,匹配結(jié)果是:成功;匹配到的內(nèi)容是:"end";匹配到的位置是:開(kāi)始于15,結(jié)束于18。
這里, . 是另一個(gè)元字符,匹配 除了換行符以外的任意字符 。 * 同樣是元字符,不過(guò)它代表的不是字符,也不是位置,而是數(shù)量——它指定* 前邊的內(nèi)容可以連續(xù)重復(fù)使用任意次以使整個(gè)表達(dá)式得到匹配 。因此, .* 連在一起就意味著 任意數(shù)量的不包含換行的字符 。?匹配 重復(fù)零次或一次 。
元字符
\s
匹配
任意的空白符,包括空格,制表符(Tab),換行符,中文全角空格等
。
\w
匹配
字母或數(shù)字或下劃線或漢字等
。
對(duì)中文/漢字的特殊處理是由.Net提供的正則表達(dá)式引擎支持的,其它環(huán)境下的具體情況請(qǐng)查看相關(guān)文檔。
和忽略大小寫(xiě)的選項(xiàng)類(lèi)似,有些正則表達(dá)式處理工具還有一個(gè)處理多行的選項(xiàng)。如果選中了這個(gè)選項(xiàng), ^ 和 $ 的意義就變成了 匹配行的開(kāi)始處和結(jié)束處 。
分枝條件
\(?0\d{2}[) -]?\d{8} 。
“(”和“)”也是元字符,后面的分組節(jié)里會(huì)提到,所以在這里需要使用轉(zhuǎn)義。
這個(gè)表達(dá)式可以匹配 幾種格式的電話號(hào)碼 ,像 (010)88886666 ,或 022-22334455 ,或 02912345678 等。
不幸的是,剛才那個(gè)表達(dá)式也能匹配 010)12345678 或 (022-87654321 這樣的“不正確”的格式。要解決這個(gè)問(wèn)題,我們需要用到 分枝條件 。正則表達(dá)式里的 分枝條件 指的是有幾種規(guī)則,如果滿足其中任意一種規(guī)則都應(yīng)該當(dāng)成匹配,具體方法是用 | 把不同的規(guī)則分隔開(kāi)。聽(tīng)不明白?沒(méi)關(guān)系,看例子:
0\d{2}-\d{8}|0\d{3}-\d{7} 這個(gè)表達(dá)式能 匹配兩種以連字號(hào)分隔的電話號(hào)碼:一種是三位區(qū)號(hào),8位本地號(hào)(如010-12345678),一種是4位區(qū)號(hào),7位本地號(hào)(0376-2233445) 。
\(?0\d{2}\)?[- ]?\d{8}|0\d{2}[- ]?\d{8} 這個(gè)表達(dá)式 匹配3位區(qū)號(hào)的電話號(hào)碼,其中區(qū)號(hào)可以用小括號(hào)括起來(lái),也可以不用,區(qū)號(hào)與本地號(hào)間可以用連字號(hào)或空格間隔,也可以沒(méi)有間隔 。你可以試試用分枝條件把這個(gè)表達(dá)式擴(kuò)展成也支持4位區(qū)號(hào)的。
\d{5}-\d{4}|\d{5} 這個(gè)表達(dá)式用于匹配美國(guó)的郵政編碼。美國(guó)郵編的規(guī)則是5位數(shù)字,或者用連字號(hào)間隔的9位數(shù)字。之所以要給出這個(gè)例子是因?yàn)樗苷f(shuō)明一個(gè)問(wèn)題: 使用分枝條件時(shí),要注意各個(gè)條件的順序 。如果你把它改成 \d{5}|\d{5}-\d{4} 的話,那么就只會(huì)匹配5位的郵編(以及9位郵編的前5位)。原因是匹配分枝條件時(shí),將會(huì)從左到右地測(cè)試每個(gè)條件,如果滿足了某個(gè)分枝的話,就不會(huì)去再管其它的條件了。
反義
代碼/語(yǔ)法 說(shuō)明
\W | 匹配任意不是字母,數(shù)字,下劃線,漢字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非數(shù)字的字符 |
\B | 匹配不是單詞開(kāi)頭或結(jié)束的位置 |
[^x] | 匹配除了x以外的任意字符 |
[^aeiou] | 匹配除了aeiou這幾個(gè)字母以外的任意字符 |
例:<a[^>]+> 匹配 用尖括號(hào)括起來(lái)的以a開(kāi)頭的字符串 。
分組與后向引用
可以用小括號(hào)來(lái)指定 子表達(dá)式 (也叫做 分組 ),然后你就可以指定這個(gè)子表達(dá)式的重復(fù)次數(shù)了。例如: 描述一個(gè)正確的IP地址: ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) 。
使用小括號(hào)指定一個(gè)子表達(dá)式后,匹配這個(gè)子表達(dá)式的文本(也就是此分組捕獲的內(nèi)容)可以在表達(dá)式或其它程序中作進(jìn)一步的處理。默認(rèn)情況下,每個(gè)分組會(huì)自動(dòng)擁有一個(gè) 組號(hào) , 分組0對(duì)應(yīng)整個(gè)正則表達(dá)式。 后向引用 用于重復(fù)搜索前面某個(gè)分組匹配的文本。例如, \1 代表 分組1匹配的文本 。 \b(\w+)\b\s+\1\b 可以用來(lái)匹配 重復(fù)的單詞 ,像 go go , 或者 kitty kitty 。
實(shí)際上組號(hào)分配過(guò)程是要從左向右掃描兩遍的:第一遍只給未命名組分配,第二遍只給命名組分配--因此所有命名組的組號(hào)都大于未命名的組號(hào), 你可以使用 (?:exp) 這樣的語(yǔ)法來(lái)剝奪一個(gè)分組對(duì)組號(hào)分配的參與權(quán)。
你也可以自己指定子表達(dá)式的 組名 。 (?<Word>\w+) (或者把尖括號(hào)換成 ' 也行: (?'Word'\w+) ),這樣就把 \w+ 的組名指定為 Word 了。要反向引用這個(gè)分組 捕獲 的內(nèi)容,你可以使用 \k<Word> ,所以上一個(gè)例子也可以寫(xiě)成這樣: \b(?<Word>\w+)\b\s+\k<Word>\b 。
使用小括號(hào)的時(shí)候,還有很多特定用途的語(yǔ)法。下面列出了最常用的一些:
分類(lèi) 代碼/語(yǔ)法 說(shuō)明 捕獲 零寬斷言 注釋
(exp) | 匹配exp,并捕獲文本到自動(dòng)命名的組里 |
(?<name>exp) | 匹配exp,并捕獲文本到名稱(chēng)為name的組里,也可以寫(xiě)成(?'name'exp) |
(?:exp) | 匹配exp,不捕獲匹配的文本,也不給此分組分配組號(hào) |
(?=exp) | 匹配exp前面的位置 |
(?<=exp) | 匹配exp后面的位置 |
(?!exp) | 匹配后面跟的不是exp的位置 |
(?<!exp) | 匹配前面不是exp的位置 |
(?#comment) |
這種類(lèi)型的分組不對(duì)正則表達(dá)式的處理產(chǎn)生任何影響,用于提供注釋讓人閱讀
|
零寬斷言
斷言用來(lái)聲明一個(gè)應(yīng)該為真的事實(shí)。正則表達(dá)式中只有當(dāng)斷言為真時(shí)才會(huì)繼續(xù)進(jìn)行匹配。 接下來(lái)的四個(gè)用于查找在某些內(nèi)容(但并不包括這些內(nèi)容)之前或之后的東西,也就是說(shuō)它們像 \b , ^ , $ 那樣用于指定一個(gè)位置,這個(gè)位置應(yīng)該滿足一定的條件(即斷言),因此它們也被稱(chēng)為 零寬斷言 。
(?=exp) 也叫 零寬度正預(yù)測(cè)先行斷言 ,它 斷言自身出現(xiàn)的位置的后面能匹配表達(dá)式exp 。比如 \b\w+(?=ing\b) ,匹配 以ing結(jié)尾的單詞的前面部分(除了ing以外的部分) ,如查找 I'm singing while you're dancing. 時(shí),它會(huì)匹配 sing 和 danc 。 \b(?=\w{7}\b)\w*clip\w*\b ,匹配包含clip的七個(gè)字符的單詞。
(?<=exp) 也叫 零寬度正回顧后發(fā)斷言 ,它 斷言自身出現(xiàn)的位置的前面能匹配表達(dá)式exp 。比如 (?<=\bre)\w+\b 會(huì)匹配 以re開(kāi)頭的單詞的后半部分(除了re以外的部分) ,例如在查找 reading a book 時(shí),它匹配 ading 。
假如你想要給一個(gè)很長(zhǎng)的數(shù)字中每三位間加一個(gè)逗號(hào)(當(dāng)然是從右邊加起了),你可以這樣查找需要在前面和里面添加逗號(hào)的部分: ((?<=\d)\d{3})+\b ,用它對(duì) 1234567890 進(jìn)行查找時(shí)結(jié)果是 234567890 。
下面這個(gè)例子同時(shí)使用了這兩種斷言: (?<=\s)\d+(?=\s) 匹配 以空白符間隔的數(shù)字(再次強(qiáng)調(diào),不包括這些空白符) 。
負(fù)向零寬斷言
前面我們提到過(guò)怎么查找 不是某個(gè)字符或不在某個(gè)字符類(lèi)里 的字符的方法(反義)。但是如果我們只是想要 確保某個(gè)字符沒(méi)有出現(xiàn),但并不想去匹配它 時(shí)怎么辦?例如,如果我們想查找這樣的單詞--它里面出現(xiàn)了字母q,但是q后面跟的不是字母u,我們可以嘗試這樣:
\b\w*q[^u]\w*\b 匹配 包含 后面不是字母u的字母q 的單詞 。但是如果多做測(cè)試(或者你思維足夠敏銳,直接就觀察出來(lái)了),你會(huì)發(fā)現(xiàn),如果q出現(xiàn)在單詞的結(jié)尾的話,像 Iraq , Benq ,這個(gè)表達(dá)式就會(huì)出錯(cuò)。這是因?yàn)? [^u] 總要匹配一個(gè)字符,所以如果q是單詞的最后一個(gè)字符的話,后面的 [^u] 將會(huì)匹配q后面的單詞分隔符(可能是空格,或者是句號(hào)或其它的什么),后面的 \w*\b 將會(huì)匹配下一個(gè)單詞,于是 \b\w*q[^u]\w*\b 就能匹配整個(gè) Iraq fighting 。 負(fù)向零寬斷言 能解決這樣的問(wèn)題,因?yàn)樗黄ヅ湟粋€(gè)位置,并不 消費(fèi) 任何字符。現(xiàn)在,我們可以這樣來(lái)解決這個(gè)問(wèn)題: \b\w*q(?!u)\w*\b 。
零寬度負(fù)預(yù)測(cè)先行斷言 (?!exp) , 斷言此位置的后面不能匹配表達(dá)式exp 。例如: \d{3}(?!\d) 匹配 三位數(shù)字,而且這三位數(shù)字的后面不能是數(shù)字 ; \b((?!abc)\w)+\b 匹配 不包含連續(xù)字符串a(chǎn)bc的單詞 。
同理,我們可以用 (?<!exp) , 零寬度負(fù)回顧后發(fā)斷言 來(lái) 斷言此位置的前面不能匹配表達(dá)式exp : (?<![a-z])\d{7} 匹配 前面不是小寫(xiě)字母的七位數(shù)字 。
請(qǐng)?jiān)敿?xì)分析表達(dá)式 (?<=<(\w+)>).*(?=<\/\1>) ,這個(gè)表達(dá)式最能表現(xiàn)零寬斷言的真正用途。
一個(gè)更復(fù)雜的例子: (?<=<(\w+)>).*(?=<\/\1>) 匹配 不包含屬性的簡(jiǎn)單HTML標(biāo)簽內(nèi)里的內(nèi)容 。 (?<=<(\w+)>) 指定了這樣的 前綴 : 被尖括號(hào)括起來(lái)的單詞 (比如可能是<b>),然后是 .* (任意的字符串),最后是一個(gè) 后綴 (?=<\/\1>) 。注意后綴里的 \/ ,它用到了前面提過(guò)的字符轉(zhuǎn)義; \1 則是一個(gè)反向引用,引用的正是 捕獲的第一組 ,前面的 (\w+) 匹配的內(nèi)容,這樣如果前綴實(shí)際上是<b>的話,后綴就是</b>了。整個(gè)表達(dá)式匹配的是<b>和</b>之間的內(nèi)容(再次提醒,不包括前綴和后綴本身)。
貪婪與懶惰
當(dāng)正則表達(dá)式中包含能接受重復(fù)的限定符時(shí),通常的行為是(在使整個(gè)表達(dá)式能得到匹配的前提下)匹配 盡可能多 的字符。以這個(gè)表達(dá)式為例: a.*b ,它將會(huì)匹配 最長(zhǎng)的以a開(kāi)始,以b結(jié)束的字符串 。如果用它來(lái)搜索 aabab 的話,它會(huì)匹配整個(gè)字符串 aabab 。這被稱(chēng)為 貪婪 匹配。
有時(shí),我們更需要 懶惰 匹配,也就是匹配 盡可能少 的字符。前面給出的限定符都可以被轉(zhuǎn)化為懶惰匹配模式,只要在它后面加上一個(gè)問(wèn)號(hào) ? 。這樣 .*? 就意味著 匹配任意數(shù)量的重復(fù),但是在能使整個(gè)匹配成功的前提下使用最少的重復(fù) ?,F(xiàn)在看看懶惰版的例子吧:
a.*?b 匹配 最短的,以a開(kāi)始,以b結(jié)束的字符串 。如果把它應(yīng)用于 aabab 的話,它會(huì)匹配 aab(第一到第三個(gè)字符) 和 ab(第四到第五個(gè)字符) 。
為什么第一個(gè)匹配是aab(第一到第三個(gè)字符)而不是ab(第二到第三個(gè)字符)?簡(jiǎn)單地說(shuō),因?yàn)檎齽t表達(dá)式有另一條規(guī)則,比懶惰/貪婪規(guī)則的優(yōu)先級(jí)更高:最先開(kāi)始的匹配擁有最高的優(yōu)先權(quán)——The match that begins earliest wins。
代碼/語(yǔ)法 說(shuō)明
*? | 重復(fù)任意次,但盡可能少重復(fù) |
+? | 重復(fù)1次或更多次,但盡可能少重復(fù) |
?? | 重復(fù)0次或1次,但盡可能少重復(fù) |
{n,m}? | 重復(fù)n到m次,但盡可能少重復(fù) |
{n,}? |
重復(fù)n次以上,但盡可能少重復(fù)
|
處理選項(xiàng)
在C#中,你可以使用Regex(String, RegexOptions)構(gòu)造函數(shù)來(lái)設(shè)置正則表達(dá)式的處理選項(xiàng)。如:Regex regex = new Regex(@"\ba\w{6}\b", RegexOptions.IgnoreCase);
上面介紹了幾個(gè)選項(xiàng)如忽略大小寫(xiě),處理多行等,這些選項(xiàng)能用來(lái)改變處理正則表達(dá)式的方式。下面是.Net中常用的正則表達(dá)式選項(xiàng):
名稱(chēng) 說(shuō)明
IgnoreCase(忽略大小寫(xiě)) | 匹配時(shí)不區(qū)分大小寫(xiě)。 |
Multiline(多行模式) |
更改 ^ 和 $ 的含義,使它們分別在任意一行的行首和行尾匹配,而不僅僅在整個(gè)字符串的開(kāi)頭和結(jié)尾匹配。(在此模式下, $ 的精確含意是:匹配\n之前的位置以及字符串結(jié)束前的位置.)
例如:默認(rèn)情況下,表達(dá)式 "^" 和 "$" 只匹配字符串的開(kāi)始 ① 和結(jié)尾 ④ 位置。如:
|
Singleline(單行模式) | 默認(rèn)情況下,小數(shù)點(diǎn) "." 匹配除了換行符(\n)以外的字符。配置為 Singleline 可使小數(shù)點(diǎn)可匹配包括換行符在內(nèi)的所有字符。 |
IgnorePatternWhitespace(忽略空白) | 忽略表達(dá)式中的非轉(zhuǎn)義空白并啟用由 # 標(biāo)記的注釋。 |
ExplicitCapture(顯式捕獲) | 僅捕獲已被顯式命名的組。 |
一個(gè)經(jīng)常被問(wèn)到的問(wèn)題是:是不是只能同時(shí)使用多行模式和單行模式中的一種?答案是:不是。 這兩個(gè)選項(xiàng)之間沒(méi)有任何關(guān)系 ,除了它們的名字比較相似(以至于讓人感到疑惑)以外。
其它語(yǔ)法
\a | 報(bào)警字符(打印它的效果是電腦嘀一聲) |
\b | 通常是單詞分界位置,但如果在字符類(lèi)里使用代表退格 |
\t | 制表符,Tab |
\r | 回車(chē) |
\v | 豎向制表符 |
\f | 換頁(yè)符 |
\n | 換行符 |
\e | Escape |
\0nn | ASCII代碼中八進(jìn)制代碼為nn的字符 |
\xnn | ASCII代碼中十六進(jìn)制代碼為nn的字符 |
\unnnn | Unicode代碼中十六進(jìn)制代碼為nnnn的字符 |
\cN | ASCII控制字符。比如\cC代表Ctrl+C |
\A | 字符串開(kāi)頭(類(lèi)似^,但不受處理多行選項(xiàng)的影響) |
\Z | 字符串結(jié)尾或行尾(不受處理多行選項(xiàng)的影響) |
\z | 字符串結(jié)尾(類(lèi)似$,但不受處理多行選項(xiàng)的影響) |
\G | 當(dāng)前搜索的開(kāi)頭 |
\p{name} | Unicode中命名為name的字符類(lèi),例如\p{IsGreek} |
(?>exp) | 貪婪子表達(dá)式 |
(?<x>-<y>exp) | 平衡組 |
(?im-nsx:exp) | 在子表達(dá)式exp中改變處理選項(xiàng) |
(?im-nsx) | 為表達(dá)式后面的部分改變處理選項(xiàng) |
(?(exp)yes|no) | 把exp當(dāng)作零寬正向先行斷言,如果在這個(gè)位置能匹配,使用yes作為此組的表達(dá)式;否則使用no |
(?(exp)yes) | 同上,只是使用空表達(dá)式作為no |
(?(name)yes|no) | 如果命名為name的組捕獲到了內(nèi)容,使用yes作為表達(dá)式;否則使用no |
(?(name)yes) | 同上,只是使用空表達(dá)式作為no |
實(shí)戰(zhàn)--牛刀小試


.alert(
'提示',
'請(qǐng)?jiān)谙吕斜碇羞x擇一條消息!');

New: 字符串替換實(shí)例(Java)
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
