正規表示式30分鐘入門教程 2

2021-09-05 21:12:53 字數 4712 閱讀 2437

有時需要查詢不屬於某個能簡單定義的字元類的字元。比如想查詢除了數字以外,其它任意字元都行的情況,這時需要用到反義:

表3.常用的反義** **/語法 說明

/w 匹配任意不是字母,數字,下劃線,漢字的字元

/s 匹配任意不是空白符的字元

/d 匹配任意非數字的字元

/b 匹配不是單詞開頭或結束的位置

[^x] 匹配除了x以外的任意字元

[^aeiou] 匹配除了aeiou這幾個字母以外的任意字元

例子:/s+匹配不包含空白符的字串。

]+>匹配用尖括號括起來的以a開頭的字串。

後向引用

使用小括號指定乙個子表示式後,匹配這個子表示式的文字(也就是此分組捕獲的內容)可以在表示式或其它程式中作進一步的處理。預設情況下,每個分組會自動擁有乙個組號,規則是:從左向右,以分組的左括號為標誌,第乙個出現的分組的組號為1,第二個為2,以此類推。

呃……其實,組號分配還不像我剛說得那麼簡單:

分組0對應整個正規表示式

實際上組號分配過程是要從左向右掃瞄兩遍的:第一遍只給未命名組分配,第二遍只給命名組分配--因此所有命名組的組號都大於未命名的組號

你可以使用(?:exp)這樣的語法來剝奪乙個分組對組號分配的參與權.

後向引用用於重複搜尋前面某個分組匹配的文字。例如,/1代表分組1匹配的文字。難以理解?請看示例:

/b(/w+)/b/s+/1/b可以用來匹配重複的單詞,像go go, 或者kitty kitty。這個表示式首先是乙個單詞,也就是單詞開始處和結束處之間的多於乙個的字母或數字(/b(/w+)/b),這個單詞會**獲到編號為1的分組中,然後是1個或幾個空白符(/s+),最後是分組1中捕獲的內容(也就是前面匹配的那個單詞)(/1)。

你也可以自己指定子表示式的組名。要指定乙個子表示式的組名,請使用這樣的語法:(?/w+)(或者把尖括號換成'也行:(?'word'/w+)),這樣就把/w+的組名指定為word了。要反向引用這個分組捕獲的內容,你可以使用/k,所以上乙個例子也可以寫成這樣:/b(?/w+)/b/s+/k/b。

使用小括號的時候,還有很多特定用途的語法。下面列出了最常用的一些:

表4.常用分組語法 分類 **/語法 說明

捕獲 (exp) 匹配exp,並捕獲文字到自動命名的組裡

(?exp) 匹配exp,並捕獲文字到名稱為name的組裡,也可以寫成(?'name'exp)

(?:exp) 匹配exp,不捕獲匹配的文字,也不給此分組分配組號

零寬斷言 (?=exp) 匹配exp前面的位置

(?<=exp) 匹配exp後面的位置

(?!exp) 匹配後面跟的不是exp的位置

(?我們已經討論了前兩種語法。第三個(?:exp)不會改變正規表示式的處理方式,只是這樣的組匹配的內容不會像前兩種那樣**獲到某個組裡面,也不會擁有組號。「我為什麼會想要這樣做?」——好問題,你覺得為什麼呢?

零寬斷言

地球人,是不是覺得這些術語名稱太複雜,太難記了?我也有同感。知道有這麼一種東西就行了,它叫什麼,隨它去吧!人若無名,便可專心練劍;物若無名,便可隨意取捨……

接下來的四個用於查詢在某些內容(但並不包括這些內容)之前或之後的東西,也就是說它們像/b,^,$那樣用於指定乙個位置,這個位置應該滿足一定的條件(即斷言),因此它們也被稱為零寬斷言。最好還是拿例子來說明吧:

斷言用來宣告乙個應該為真的事實。正規表示式中只有當斷言為真時才會繼續進行匹配。

(?=exp)也叫零寬度正**先行斷言,它斷言自身出現的位置的後面能匹配表示式exp。比如/b/w+(?=ing/b),匹配以ing結尾的單詞的前面部分(除了ing以外的部分),如查詢i'm singing while you're dancing.時,它會匹配sing和danc。

(?<=exp)也叫零寬度正回顧後發斷言,它斷言自身出現的位置的前面能匹配表示式exp。比如(?<=/bre)/w+/b會匹配以re開頭的單詞的後半部分(除了re以外的部分),例如在查詢reading a book時,它匹配ading。

假如你想要給乙個很長的數字中每三位間加乙個逗號(當然是從右邊加起了),你可以這樣查詢需要在前面和裡面新增逗號的部分:((?<=/d)/d)+/b,用它對1234567890進行查詢時結果是234567890。

下面這個例子同時使用了這兩種斷言:(?<=/s)/d+(?=/s)匹配以空白符間隔的數字(再次強調,不包括這些空白符)。

負向零寬斷言

前面我們提到過怎麼查詢不是某個字元或不在某個字元類裡的字元的方法(反義)。但是如果我們只是想要確保某個字元沒有出現,但並不想去匹配它時怎麼辦?例如,如果我們想查詢這樣的單詞--它裡面出現了字母q,但是q後面跟的不是字母u,我們可以嘗試這樣:

/b/w*q[^u]/w*/b匹配包含後面不是字母u的字母q的單詞。但是如果多做測試(或者你思維足夠敏銳,直接就觀察出來了),你會發現,如果q出現在單詞的結尾的話,像iraq,benq,這個表示式就會出錯。這是因為[^u]總要匹配乙個字元,所以如果q是單詞的最後乙個字元的話,後面的[^u]將會匹配q後面的單詞分隔符(可能是空格,或者是句號或其它的什麼),後面的/w*/b將會匹配下乙個單詞,於是/b/w*q[^u]/w*/b就能匹配整個iraq fighting。負向零寬斷言能解決這樣的問題,因為它只匹配乙個位置,並不消費任何字元。現在,我們可以這樣來解決這個問題:/b/w*q(?!u)/w*/b。

零寬度負**先行斷言(?!exp),斷言此位置的後面不能匹配表示式exp。例如:/d(?!/d)匹配三位數字,而且這三位數字的後面不能是數字;/b((?!abc)/w)+/b匹配不包含連續字串abc的單詞。

同理,我們可以用(?請詳細分析表示式(?<=<(/w+)>).*(?=),這個表示式最能表現零寬斷言的真正用途。

乙個更複雜的例子:(?<=<(/w+)>).*(?=)匹配不包含屬性的簡單html標籤內裡的內容。(<?(/w+)>)指定了這樣的字首:被尖括號括起來的單詞(比如可能是),然後是.*(任意的字串),最後是乙個字尾(?=)。注意字尾裡的//,它用到了前面提過的字元轉義;/1則是乙個反向引用,引用的正是捕獲的第一組,前面的(/w+)匹配的內容,這樣如果字首實際上是的話,字尾就是了。整個表示式匹配的是之間的內容(再次提醒,不包括字首和字尾本身)。

注釋小括號的另一種用途是通過語法(?#comment)來包含注釋。例如:2[0-4]/d(?#200-249)|25[0-5](?#250-255)|[01]?/d/d?(?#0-199)。

要包含注釋的話,最好是啟用「忽略模式裡的空白符」選項,這樣在編寫表示式時能任意的新增空格,tab,換行,而實際使用時這些都將被忽略。啟用這個選項後,在#後面到這一行結束的所有文字都將被當成注釋忽略掉。例如,我們可以前面的乙個表示式寫成這樣:

(?<=    # 斷言要匹配的文字的字首

<(/w+)> # 查詢尖括號括起來的字母或數字(即html/xml標籤)

)       # 字首結束

.*      # 匹配任意文字

(?=     # 斷言要匹配的文字的字尾

# 查詢尖括號括起來的內容:前面是乙個"/",後面是先前捕獲的標籤

)       # 字尾結束貪婪與懶惰

當正規表示式中包含能接受重複的限定符時,通常的行為是(在使整個表示式能得到匹配的前提下)匹配盡可能多的字元。以這個表示式為例:a.*b,它將會匹配最長的以a開始,以b結束的字串。如果用它來搜尋aabab的話,它會匹配整個字串aabab。這被稱為貪婪匹配。

有時,我們更需要懶惰匹配,也就是匹配盡可能少的字元。前面給出的限定符都可以被轉化為懶惰匹配模式,只要在它後面加上乙個問號?。這樣.*?就意味著匹配任意數量的重複,但是在能使整個匹配成功的前提下使用最少的重複。現在看看懶惰版的例子吧:

a.*?b匹配最短的,以a開始,以b結束的字串。如果把它應用於aabab的話,它會匹配aab(第一到第三個字元)和ab(第四到第五個字元)。

為什麼第乙個匹配是aab(第一到第三個字元)而不是ab(第二到第三個字元)?簡單地說,因為正規表示式有另一條規則,比懶惰/貪婪規則的優先順序更高:最先開始的匹配擁有最高的優先權——the match that begins earliest wins。

表5.懶惰限定符 **/語法 說明

*? 重複任意次,但盡可能少重複

+? 重複1次或更多次,但盡可能少重複

?? 重複0次或1次,但盡可能少重複

? 重複n到m次,但盡可能少重複

? 重複n次以上,但盡可能少重複

處理選項

在c#中,你可以使用regex(string, regexoptions)建構函式來設定正規表示式的處理選項。如:regex regex = new regex(@"/ba/w/b", regexoptions.ignorecase);

上面介紹了幾個選項如忽略大小寫,處理多行等,這些選項能用來改變處理正規表示式的方式。下面是.net中常用的正規表示式選項:

表6.常用的處理選項 名稱 說明

ignorecase(忽略大小寫) 匹配時不區分大小寫。

multiline(多行模式) 更改^和$的含義,使它們分別在任意一行的行首和行尾匹配,而不僅僅在整個字串的開頭和結尾匹配。(在此模式下,$的精確含意是:匹配/n之前的位置以及字串結束前的位置.) 

singleline(單行模式) 更改.的含義,使它與每乙個字元匹配(包括換行符/n)。 

ignorepatternwhitespace(忽略空白) 忽略表示式中的非轉義空白並啟用由#標記的注釋。

explicitcapture(顯式捕獲) 僅捕獲已被顯式命名的組。

乙個經常被問到的問題是:是不是只能同時使用多行模式和單行模式中的一種?答案是:不是。這兩個選項之間沒有任何關係,除了它們的名字比較相似(以至於讓人感到疑惑)以外。

正規表示式30分鐘入門教程

30分鐘內讓你明白正規表示式是什麼,並對它有一些基本的了解,讓你可以在自己的程式或網頁裡使用它。最重要的是 請給我30分鐘,如果你沒有使用正規表示式的經驗,請不要試圖在30秒內入門 除非你是超人 別被下面那些複雜的表示式嚇倒,只要跟著我一步一步來,你會發現正規表示式其實並沒有你 想像中的那麼困難。當...

PHP正規表示式30分鐘入門教程

正規表示式30分鐘入門教程 三個常用的知識點 1 惰性匹配 正則引擎預設是貪婪的,若要最少重複的話,需要用到惰性匹配符 懶惰限定符 語法 說明 重複任意次,但盡可能少重複 重複1次或更多次,但盡可能少重複 重複0次或1次,但盡可能少重複 重複n到m次,但盡可能少重複 重複n次以上,但盡可能少重複 參...

正規表示式10分鐘入門教程

編寫驗證規則最流行和最簡單的方法就是正規表示式了,但唯一的乙個問題是正規表示式的語法太隱晦了,讓人蛋疼無比。很多開發者為了在專案中應用複雜的驗證,經常要使用一些小抄來記住正則式的複雜語法和各種常用命令。在這篇文章中,我將試圖讓大家明白什麼是正規表示式,以及如何更輕鬆地學習正規表示式。正規表示式可以幫...