C#中的Regex(1)
最後更新:2017-02-28
來源:互聯網
上載者:User
正則 C#中的Regex
Jeffrey E.F. Friedl寫了一本關於Regex的書《精通Regex》。作者為了使讀者更好的理解和掌握Regex,編造了一個故事。該書的語言以perl為主。據我所知C#中的Regex也是基於perl5。所以它們應該有許多的共同之處。
其實,我並不打算原封不動的對該書的內容進行翻譯,一則這本書內容太多了,我根本就不勝任翻譯這項工作;二則如果我真的把這本書翻譯過來,同時把裡面的代碼換成C#,在沒有徵得原作者的情況下,可能有侵權的嫌疑了。所以,權當作讀書筆記好了。
略過冗長的前言,我們可以直接進入第一章:
介紹Regex
作者說這一章是為Regex的絕對菜鳥而準備的,目的是為以後的章節打下堅實的基礎。那麼如果你是不是菜鳥,你可以忽略這一章。
故事情境:
你的檔案部的頭兒想要一個工具用來檢查重複的單詞(如:this this),一個在大量編輯文檔的時候通常會遇到的問題。你的工作就是建立一個解決方案:
接受任何數量要檢查的檔案,報告每個檔案中帶有重複單詞的那些行,反白這些重複的單詞,同時確保原檔案名稱和這些行出現在報表中。
跨行檢查,找到一行的最後一個單詞和下一行開頭第一個單詞出現重複的情況。
找出重複的單詞,不管他們是否大小寫不同(如:The the),以及允許在這些重複單詞之間含有不同數量的空白字元(空格、定位字元、新行等)
找出重複的單詞,甚至這些單詞被Html標籤隔開。(如:…it is <B>very</B> very important.)
要解決上述的實際問題,我們首先要做的就是寫出Regex,找到我們想要的文本,忽略我們不需要的文本,然後使用我們的C#代碼對擷取的文本進行處理。
在使用Regex之前,你也許多少已經知道什麼是Regex。甚至你不知道,你幾乎可以肯定已經熟悉它的基本概念了。
你知道report.txt是一個具體的檔案名稱,但是如果你有任何Unix或者DOS/Windows的經驗,你也知道“*.txt”可以用來選擇多個檔案。這種形式的檔案名稱,有一些字元有著特殊的含義。星號意味著匹配任何東西,問號意味著匹配一個字元。如:“*.txt”表示任何檔案名稱以.txt結尾的檔案。
檔案名稱得模式比對,使用了有限的匹配符。還有當前網路上的搜尋引擎也允許使用某些指定的匹配符來進行內容搜尋。Regex採用豐富的匹配字元,可以處理各種複雜的問題。
首先我們介紹兩個位置匹配符:
^ : 表示一行文字的開始位置
$ : 表示一行文字的結束位置
如:運算式:"^Cat", 匹配的單詞Cat出現在行的開始處,注意^是一個位置字元,不是要匹配字元的本身。
同樣,運算式:"Cat$" 匹配的單詞Cat出現來一行的結尾處。
接下來,我們介紹運算式中的方括弧"[]", 它表示匹配括弧中字元中的一個。如:
運算式:"[0123456789]"將匹配數字0到9的任何一個。
例如:我們要尋找文本中,所有包含gray或者grey,那麼運算式可以這麼寫:"gr[ea]y"
[ea]表示匹配ea中的一個,而不是整個ea。
如果我們要匹配html中的<H1><H2><H3><H4><H5><H6>的標籤,我們可以寫運算式:
"<H[123456]>",但是如果我們要匹配所有字元中的一個呢?哈,問題就來了,在方括弧中寫出所有的字元?很幸運,我們不必這麼做,我們引進範圍符號"-";
使用範圍符號,我們只需要給出一個範圍的邊界字元即可,上面的Html例子,我們可以寫成:"<H[1-6]>"
而運算式:"[0-9a-zA-Z]"的意思現在清楚了吧?它匹配數字字元,小寫26個字母和大寫26個字母中的一個。
出現在[]中的"^"符號
如果你看到運算式如:"[^0-9]",此時,"^"不再是前面說的位置符號,這裡它是否定符號,表示排除的意思,上面的運算式,表示不包含數字0到9的字元。
思考1:運算式"q[^u]"的意思。假如有下列的單詞,那些將被匹配?
Iraqi
Iraqian
miqra
qasida
qintar
qoph
zaqqum
除了範圍字元的表示之外,還有一個是點字元".",點字元出現在運算式中,表示匹配任何字元。
如運算式:"07.04.76"將匹配:
形如:07/04/76, 07-04-76,07.04.76。
如果我們需要在某些字元中可選擇,我們可以採用選項字元"|":
選項字元有“或"的意思,比如運算式:"[Bob|Robert]"則表示Bob或者Robert將被匹配。
現在看我們前面提到的運算式:"gr[ea]y" ,利用選項字元我們可以寫作"grey|gray",它們是相同的。
圓括弧的使用:圓括弧在運算式中也是被作為元字元使用,如前面的運算式,我們可以寫成:"gr(e|a)y",這裡的圓括弧是必須的,如果沒有圓括弧,那麼運算式"gre|ay"將匹配gre或者ay,這不是我們想要的結果。如果你還不是很清楚,讓我們看一下下面的例子:
在電子郵件中尋找所有以From:或者Subject:或者Date:開頭的行,我們比較下面的兩個運算式:
運算式1:"^From|Subject|Data: "
運算式2:"^(From|Subject|Data): "
哪一個是我們想要的?
很明顯,運算式1的結果不是我們想要的結果,它匹配的將是:From或者Subjec或者Data: ,運算式2使用圓括符,就能滿足我們的需要。
單詞邊界
我們已經可以匹配出現在行首和行尾的字元,那麼如果我們想定位的不僅僅是行首或者行尾呢?我們需要引入單詞邊界符號,單詞邊界符號是:"\b",斜杠不可省略,否則變成匹配字母b。使用單詞邊界符號,我們可以定位匹配的位置必須出現在一個單詞的開始或者結尾部分,而不是在單詞的中間。例如:"\bis\b"運算式在字串"This is a cat."中將匹配單詞"is"而不會匹配單詞"This"中的"is"。
字串邊界符號
除了上述的位置符號,如果我們要匹配的是整個字串(含多個單詞)那麼我們可以使用下面的兩個符號:
\A :表示字串的開始處;
\z :表示字串的結束處。
運算式:"\AThis is a cat\z"將匹配這個字串"This is a cat"。
使用邊界定位器號,這裡要提到一個重要的概念,那就是單詞字元,單詞字元表示可以構成單詞的字元,它們是[a-zA-Z0-9]中的任意一個字元。所以上面的運算式也會在句子"This is a cat."得到匹配。匹配的結果不包含句號。
重複數量符號
讓我們看錶達式:"Colou?r", 這個運算式中出現了我們還沒有見過的問號,(這個問號和檔案名稱匹配的問號意義不同),它表示符號前面的一個字元可以被重複的次數,"?"表示0次或者1次,前面的運算式中問號表示u可以出現0或1次,所以它將匹配"Color"或者"Colour"。
下面是其他的重複數量符號:
+ :表示1次或者多次
* :表示0次或者多次
例如我們要表示一或多個空格,我們可以寫運算式:" +";
如果要表示具體次數呢?我們引入花括符{}。
{n} : n是具體的數字,表示重複n次。
{n,m}: 表示最少那次,最多m次。
這些符號都限定了符號前面一個字元的匹配次數。但是如果你想重複多個字元,比如一個單詞,那麼怎麼辦?我們再次使用圓括弧,前面我們把圓括弧作為選項的範圍符號,這裡是圓括的另外一種使用方法,它被表示為一個組,例如運算式:“(this)"這裡的this就是一個組,那麼問題就好辦了,重複數量符號可以用來表示它前面一個組的重複次數。
現在回到尋找重複單詞的問題,假如我們要找到“the the”,根據我們迄今為止學到的知識,我們可以寫出運算式:
"\bthe +the\b"
運算式的意思是匹配兩個the中間有一個或多個空格隔開。
同樣,我們還可以寫成:
"\b(the +){2}"
但是如果要找全部可能的重複單詞呢?我們目前的知識還不足以解決這個問題,下面我們引進反向引用的概念,我們已經看到圓括弧可以作為組的邊界,一個運算式中可以有多個被圓括弧限定的組,根據它們出現的次序,這些組預設的被分配了一個組號,第一個出現的組號為1號,依次類推。那麼反向引用就是可以在之後的運算式的位置上是使用"\n"來引用這個組,這裡n是被引用的組號。反向引用就像是程式中的變數一樣,下面我們看具體的例子:
前面的單詞重複表格達式,現在我們採用反向引用可以寫做:
"\b(the) +\1\b"
現在,如果我們要匹配所有的重複單詞,我們就可以改寫運算式為:
"\b([a-zA-Z]+) +\1\b"
最後一個問題是,如果我們要匹配的字元是Regex中的符號,怎麼辦?對,使用轉義符號"\", 例如如果你要匹配一個小數點,那麼你可以:"\.",還要注意的是如果在程式中使用運算式那麼"\"也要按照字串的規定變成"\\"或者在運算式前面加@。
本章僅僅是提供給菜鳥一個關於Regex的基礎知識,它只是其中的部分,我們還有許多東西要學習,這將在後面的章節中一一介紹。其實,Regex的學習並不難,你需要的是耐心和實踐,如果你想精通它的話。或許有人說:“我不想知道汽車的細節,我只想學會怎麼開車。”如果你也是這樣想的,那麼,你永遠也不知道怎麼使用Regex來解決你的問題,進而,你也永遠不會懂得Regex的真正的強大。