正則表達式(Regular Expression)也叫匹配模式(Pattern),用來檢驗字符串是否知足特定規則,或從字符串中捕獲知足特定規則的子串。正則表達式的命名空間是System.Text.RegularExpressions,Regex類即正則表達式。正則表達式
1.字符匹配express
最簡單的正則表達式由「普通字符」和「通配符」組成。好比「Room\d\d\d」就這樣的正則表達式。學習
這些特殊字符在正則表達式中稱爲元字符,由於符號「.」在正則表達式裏已有特殊用處,因此要想表達「.」自己,需使用它的轉移字符「\.」,要表達符號「\」自己,一樣,需使用它的轉移字符「\\」。 .NET 提供了一批與正則表達式相關的類,它們都位於System.Text.RegularExpressions命名空間,如今咱們來學習最主要的Regex 類,並用它來匹配正則表達式。Regex 類的部分方法如表所示:spa
經過 Regex.Matches()方法,能夠從給定字符串中分解出全部與正則表達式匹配的子串,這些子串被保存在一個MatchCollection 型集合中,每一個子串都被看做Match 類的對象。.net
如今假設某份電子文件裏包含着 Kitty 的房間號碼(格式爲RoomXXX),檔案很長,人工查閱很耗時間,那麼如何經過計算機幫咱們找到房間號碼呢?code
string text = "The Colony is a beautiful town...Kitty lives in Room415...";orm
//正則表達式對象
Regex expression = new Regex(@"Room\d\d\d");blog
//找出全部匹配的字符串,放入集合中。字符串
MatchCollection matches = expression.Matches(text);
//輸出匹配的字符串
foreach (Match match in matches)
{ Console.WriteLine("She lives in {0}", match); }
@前綴和轉義字符,前面學習過控制文本格式的轉義字符,如「\n」、「\"」、「\t」、「\\」等;如今咱們又學習了正則表達式的轉義字符,如「\.」、「\w」、「\d」 、「\s」 、「\\」等。在正則表達式中它們二者是有區別的。相似Regex expression = new Regex("\d");這樣的語句是錯誤的,由於反斜槓「\」自己就是一個很特殊的字符,要想表示反斜槓自己,須要使用它的轉移字符「\\」,因此須要寫成下面的形式:Regex expression = new Regex("\\d");但這種形式會下降可讀性,因此咱們一般使用添加@前綴的方式。Regex expression = new Regex(@"\d");這時會忽略控制文本格式的轉義字符,但不忽略正則表達式的轉義字符。添加前綴@後,若是字符串裏須要雙引號自己,能夠用兩個連續的雙引號表示。Regex expression = new Regex(@"Say ""Hello"" to Heaven");。
2.可選的字符集
通符符限定了某個位置上匹配的某一類型字符,但有時還想縮小匹配的範圍,限定到某一類型的某一範圍,這裏能夠把某個位置上容許出現的字符寫在方括號[]內,組成可選字符集,好比:
[abc]表示該位置能夠出現字母a、b、c
[A-D]表示該位置能夠出現字母A、B、C、D
[A-DM-P]表示該位置能夠出現字母A 到D 或M 到P
[A-Da-d]表示該位置能夠出現字母A 到D 或a 到d
[12] 表示該位置能夠出現數字1 或數字2
[1-5]表示該位置能夠出現數字1 到5
[0-57-9]表示該位置能夠出現除6 外的全部數字
[\s\S]表示該位置能夠出現任何字符,包括任何可見字符和不可見字符(如空格、製表符、換行等)
注意,無論中括號看起來有多長,它都只表明一個字符。表達式中的[VR]、[a-z]、[89]、[0-9]都是一個字符,不能看做多個字符。例如:
string text = "Vitor-1970 Verne-1982 Regan-1998 Robin2008";
//正則表達式
Regex expression = new Regex(@"[VR][a-z][a-z][a-z][a-z]-19[89][0-9]");
//獲取並輸出匹配的字符串
foreach(Match match in expression.Matches(text))
{ Console.WriteLine(match); }
由於在中括號表達式中,「[」標誌着中括號表達式的開始,而「]」標誌着中括號表達式的結束,因此要想在中括號表達式外表示它自己,需使用它的轉義字符「\[」、 「\]」,一樣「-」表示連字符,要想在中括號表達式外表示它自己,需使用它們的轉義字符「\-」。對於「-」還有不用轉義字符的方式,也能使中括號包含「-」,例如把連字符放在中括號列表的開始或結尾,好比[-1-9]或[abc-];
反向字符集,在中括號表達式用符號「^」表示非,例如[^x]表示匹配除x的全部字符,[^abc] 匹配除a、b、c 之外的全部字符。固然,當須要匹配它自己時,要在中括號表達式中使用它的轉義字符「\^」。
3.或匹配|
在正則表達式中用符號「|」表示「或」,其左右供部分與|一塊兒應放到()中。例如:
"x|y" 匹配 "x"或"y"
"good|ok" 匹配 "good"或"ok"
"(tr|b)ee" 匹配 "tree"或"bee"
"th(i|a)nk" 匹配 "think"或"thank"
"Book One|Two" 匹配 "Book One"或"Two"
"Book (One|Two)" 匹配 "Book One"或"Book Two"
由於符號「|」、「(」和「)」在正則表達式中也有特殊用處,因此當須要匹配它們自己時,也要使用它們的轉義字符「\|」、「\(」「\)」。
數量限定符有多種,它們之間有細微的區別。
數量限定符「*」將前面的字符重複0 次或屢次,而數量限定符「+」將前面的字符重複1 次或屢次,數量限定符「?」將前面的字符重複0 次或1 次,若是須要明確指定重複次數,可以使用數量限定符{n},它表示把前面的字符重複n 遍,而數量限定符{n,}表示把它前面的字符至少重複n遍, 數量限定符{n,m}把前面的字符重複n至m遍。例如:
string words = "lg log loog looog loooog looooog";
//正則表達式
Regex expression = new Regex("lo*g");
//獲取並輸出匹配的字符串
foreach (Match match in expression.Matches(words))
{ Console.WriteLine(match); }
Regex expression1 = new Regex("lo+g");
//獲取並輸出匹配的字符串
foreach (Match match in expression1.Matches(words))
{ Console.WriteLine(match); }
Regex expression2 = new Regex("lo?g");
//獲取並輸出匹配的字符串
foreach (Match match in expression2.Matches(words))
{ Console.WriteLine(match); }
//正則表達式
Regex expression = new Regex("lo{3}g");
//獲取並輸出匹配的字符串
foreach (Match match in expression.Matches(words))
{ Console.WriteLine(match);}
//正則表達式
Regex expression = new Regex("lo{3,}g");
//獲取並輸出匹配的字符串
foreach (Match match in expression.Matches(words))
{ Console.WriteLine(match);}
//正則表達式
Regex expression = new Regex("lo{2,4}g");
//獲取並輸出匹配的字符串
foreach (Match match in expression.Matches(words))
{ Console.WriteLine(match);}
以上4個輸出結果分別爲:「lg loog looog loooog looooog」、 「log loog looog loooog looooog」、「lg log」、「looog」、「looog loooog looooog」、「loog looog loooog」。
用數量限定符還能夠重複多個字符,例如:
string words = "lg leog leoeog leoeoeog leoeoeoeog leoeoeoeoeog";
//正則表達式
Regex expression = new Regex("l(eo)+g");
//獲取並輸出匹配的字符串
foreach (Match match in expression.Matches(words))
{ Console.WriteLine(match); }
正則表達式「l(eo)+g」表示字符串的第一個字符爲l,最後一個字符爲g,中間有一個或多個eo。結果爲:"leog leoeog leoeoeog leoeoeoeog leoeoeoeoeog"。
最奇妙的是這些數量限定符還能夠與通配符組合。好比通配符「\w」匹配任意單詞字符,限定符「*」表示把前面的字符重複0 次或屢次,因此正則表達式「\w*」匹配由任意單字符組成的任意長度的單詞。例如:
string girls = @"Van is 16; Vicky is 18; Vivien is 19; Vinvcent is 22";
Regex expression = new Regex(@"V\w* is \d\d");
foreach (Match match in expression.Matches(girls))
{ Console.WriteLine(match); }
結果爲:Van is 16 、Vicky is 1八、Vivien is 1九、Vinvcent is 22
貪婪和懶惰
以上限定符都是「貪婪的」(Greedy),它們會匹配儘量多的文本。若是在限定符後加上?號,它就會變成「懶惰的」(Lazy),會匹配儘量少的文本。
string words = "ab<H1>Hello World</H1>c";
//貪婪的限定符
Regex expression1 = new Regex("<.*>");
MatchCollection matchs1 = expression1.Matches(words);
Console.WriteLine("There is {0} match with greedy quantifier:",matchs1.Count);
foreach (Match match in matchs1)
{Console.WriteLine(match); }
//懶惰的限定符
Regex expression2 = new Regex("<.*?>");
MatchCollection matchs2 = expression2.Matches(words);
Console.WriteLine("There are {0} matchs with lazy quantifier:",matchs2.Count);
foreach (Match match in matchs2)
{Console.WriteLine(match);}
使用貪婪的限定符有一條長子串與之匹配,使用懶惰的限定符有兩條短子串與之匹配。因此結果分別爲:<H1>Hello World</H1>、<H1> </H1>。。一個是「{n}?」,它實際上與「{n}」是等價的,由於不論是貪婪仍是懶惰,都已經限定死了,只能重複n次。「?」是把「?」前面字母重複0 到1 次。由於「?」是貪婪的,能重複1 次就不重複0 次(實在沒有也能匹配),「??」是懶惰的,重複次數要儘可能少,因此它選擇重複0 次。
符號「*」、「+」、「?」「{」和「}」在正則表達式中也有特殊用處,因此當須要
匹配它們自己時,也要使用它們的轉義字符「\*」、「\+」、「\?」「\{」和「\}」。
5.定位符
經過定位符能夠在指定位置尋找匹配的子串。若正則表達式中使用了定位符「^」,則在整個字符串的頭部尋找匹配的子串。例如string words = "year1998 year2008 year2018"; Regex expression = new Regex(@"^year\d\d\d\d");「^」匹配字符串的開頭位置,因此「^year\d\d\d\d」表示要從字符串的開頭位置開始尋找,這裏匹配結果爲year1998。若正則表達式中使用了定位符「$」,則在整個字符串的尾部尋找匹配的子串。string words = "year1998 year2008 year2018"; Regex expression = new Regex(@"year\d\d\d\d$");匹配結果爲year2018。
若正則表達式中使用了定位符「\b」,則在字符串中每一個「單詞」的邊界尋找匹配的子串。(單詞一般以空格、段首、段尾、逗號、句號等符號做爲邊界,分隔符「-」也能夠做爲邊界。但要注意「\b」只表示單詞與邊界符號之間的「位置」,不表示邊界符號自己。)
string words = "formfordfork";
Console.Write("None anchors:");
Regex expression = new Regex(@"for\w");
foreach (Match match in expression.Matches(words))
{ Console.Write(match + "\t"); }
Console.Write("\nAnchor start:");
expression = new Regex(@"\bfor\w");
foreach (Match match in expression.Matches(words))
{ Console.Write(match + "\t"); }
Console.Write("\n Anchor end:");
expression = new Regex(@"for\w\b");
foreach (Match match in expression.Matches(words))
{ Console.Write(match + "\t");}
結果爲:form ford fork 、form、fork。
能夠看出當沒有定位符時,匹配單詞中全部符合要求的子串;當定位符\b 在前面時,在單詞的頭部尋找匹配的子串;當定位符\b 在後面時,在單詞的結尾尋找匹配的子串。經常使用正則表達式「\b\w+\b」找出一句話裏的全部單詞。
string words = "are you ok?";
Regex expression = new Regex(@"\b\w+\b");
foreach (Match match in expression.Matches(words))
{Console.WriteLine(match);}
結果爲:are you ok。
顯然,「^」和「$」在正則表達式中也有特殊用處,因此當要匹配它們自己時也需使用它們的轉義字符「\^」和「\$」。
6.分組和向後引用
括號表達式並不簡單的起着肯定範圍的做用,它同時會建立子表達式,每一個子表達式造成一個分組,並把捕獲到的與子表達式匹配的子串保存在分組中,以供未來使用。電子郵件地址一般由用戶名、二級域名、一級域名三部分構成,一般用"(\w+)@(\w+)\.(\w+)"匹配,這裏有三個括號,造成三個子表達式,就會出現三個分組。默認狀況下,每一個分組會自動擁有一個組號,規則是:從左向右,按分組左括號的出現順序進行編號,第一個分組的組號爲1,第二個爲2,以此類推。在正則表達式裏引用分組的語法爲「\number」,好比「\1」表明與分組1 匹配的子串,「\2」表明與分組2 匹配的字串,等等。存儲起來的分組有什麼做用呢?下面咱們經過一個例子來講明。不少句子裏會有重複的單詞,下面咱們就經過正則表達式找出這些重複的單詞。
string text = "you are very very good";
foreach (Match match in Regex.Matches(text, @"\b(\w+)\b \1\b"))
{ Console.WriteLine(match);}
結果爲:very very。
前面已經知道「\b(\w+)\b」匹配一個單詞,由於這裏面有一個括號,因此會捕獲一個以1 爲組號的分組,後面的「\1」就表明這個分組,表示這個位置上出現和分組1 同樣的內容。因此二者連起來就表示兩個重複的單詞。這種在後面的表達式中引用前面的分組的方式就叫作後向引用。除了匹配重複的單詞,後向引用還有一個較爲常見的應用,那就是匹配有效的HTML標籤。例如:
string words = "<h0>not valid</h1> <h2>valid</h2> <h3>not valid</h4>";
foreach (Match match in Regex.Matches(words, @"<(.*?)>.*?</\1>"))
{Console.WriteLine(match);}
結果爲:<h2>valid</h2>。
7.替換文本
正則表達式除了查找文本外,另外一項重要功能就是替換文本。經過Regex 類的Replace()方法就能以特定字符串替換原字符串中與正則表達式匹配的子串。下面經過一個例子說明如何替換文本以及如何在替換中引用分組。電話號碼格式(010)8866598七、(0769)23658945 ,能夠用正則表達式「\((\d{3,4})\)(\d{7,8})」匹配,該表達式中有兩個分組,第一個分組對應區號,第二個分組對應號碼,如今來把電話本中全部的電話都改成形如010-88665987 的格式。
string phonebook = "(010)88665987 (020)23658945 (021)88965222";
string pattern = @"\((\d{3,4})\)(\d{7,8})";
string result = Regex.Replace(phonebook, pattern, "$1-$2");
Console.WriteLine(result);
結果:010-8866598七、020-2365894五、021-88965222。
在大部分語言的正則表達式中,查找時,使用後向引用的語法爲「\number」;而在替換時,其語法爲「$number」。 例子中的Regex.Replace ()方法把與正則表達式匹配的子串替換爲「$1-$2」,其中$1 對應與分組1(即區號),$2 對應分組2(即電話號碼)。
在.NET 中使用正則表達式進行替換時,分組的命名方式爲:(?<name> subexpression)(便可以對分組顯式命名),後向引用的語法是:${name}。因此上例也能夠寫爲:
string phonebook = "(010)88665987 (020)23658945 (021)88965222";
string pattern = @"\((?<areacode>\d{3})\)(?<number>\d{8})";
string result = Regex.Replace(phonebook, pattern, "${areacode}-${number}");
Console.WriteLine(result);
8. 非捕獲分組和預查
(1)非捕獲後組?:
不少時候,咱們添加一個分組,並非爲了在後向引用中使用它,而是出於表達上的須要。好比正則表達式「(tr|b)ee」中括號,僅僅是爲了肯定範圍,使之正確匹配「tree」或「bee」。 存儲分組會花費必定的時間,若是咱們不須要存儲分組,能夠在分組內部添加元字符「?:」,把它標誌成一個非捕獲分組。"(?:tr|b)ee"。這樣就不會存儲分組了。
(2)正向預查?=和負正向預查?!
string text = @"Jack boy 29 010-88127631 Microsoft, Sally girl 22 010-88127632 Microsoft,Ben boy 26 020-65423541 Google,Merry girl 23 020-65423542 Google,Alan boy 27 021-23456851 Apple,Kitty girl 18 021-23456852 Apple.";
Console.WriteLine("The Telephone numbers of Microsoft are:");
foreach (Match match in Regex.Matches(text, @"\d{3}-\d{8}(?= Microsoft)"))
{Console.WriteLine(match);}
結果爲:010-8812763一、010-88127632。
若是僅僅使用正則表達式「\d{3}-\d{8}」,它就會匹配文本里全部的電話號碼,那麼如何讓它只匹配微軟的電話號碼呢?這時就須要在它後面的一個分組中添加元字符「(?= Microsoft)」做爲限制條件,其格式以下所示。正向預查表達式用來篩選與查詢目標表達式匹配的文本,其結果不光要和查詢目標表達式匹配,它後面跟隨的文本還要和正向預查表達式匹配。須要強調的是正向預查表達式只是用來篩選查詢目標,並不屬於查詢目標自己,不包含在最終結果中。
負正向預查與正向預查相反,查詢目標後面不能跟指定的字符串,其語法爲在分組中添加元字符「?!」。 正則表達式「\d{3}-\d{8}(?! Microsoft)」匹配那些後面不跟「 Microsoft」的電話號碼。
(3)反向預查?<=和負反向預查?<!
正向預查?=篩選查尋目標,限制條件在查詢目標的後面,反向預查?<=篩選查尋目標時,限制條件則在查詢目標的前面,這時就須要在它前面的一個分組中添加元字符「(?= Microsoft)」做爲限制條件。其使用方法例如:
string text = @"Jack boy 29 010-88127631 Microsoft, Sally girl 22 010-88127632 Microsoft,Ben boy 26 020-65423541 Google,Merry girl 23 020-65423542 Google,Alan boy 27 021-23456851 Apple,Kitty girl 18 021-23456852 Apple.";
Console.WriteLine("The Telephone numbers of girls are:");
foreach (Match match in Regex.Matches(text, @"(?<=girl \d\d )\d{3}-\d{8}"))
{Console.WriteLine(match);}
負反向預查與反向預查相反,查詢目標前面不能跟指定字符串,其語法是在分組中添加元字符「?<!」。正則表達式「(?<!girl \d\d )\d{3}-\d{8}」匹配這樣的電話號碼,它們前面的文本不能與「girl \d\d 」匹配。
總之,預查表達式的做用就是給查詢目標添加限制條件,能夠在前面,也能夠在後面,能夠正面限制,也能夠反面限制。
9.正則表達式的類
和正則表達式相關的類都在命名空間System.Text.RegularExpressions 中,上圖展現了部分經常使用正則表達式類。
string s = "aaa@163.com bbb@263.net ccc@363.cn";
MatchCollection matchs = Regex.Matches(s, @"(\w+)@(\w+)\.(\w+)");
//輸出MatchCollection中全部的Match
Console.WriteLine("全部Match爲:");
for (int i = 0; i < matchs.Count; i++)
{ Match match = matchs[i];
Console.WriteLine(match.Value); }
Macth 的Value 屬性就是匹配的字符串,每一個 Match 中都有一個Groups 屬性,裏面包含了該Match 的全部分組。其中第0 個分組是Match 自己
經過Regex 類的Options 屬性能夠設置正則表達式的選項,下表是.Net 中經常使用的正則表達式選項。
例如:
string pattern = @"Room\d{3}";
Regex expression = new Regex(pattern, RegexOptions.IgnoreCase);
若是想同時設置多個選項,能夠把它們用按位或「|」鏈接起來。
new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
11.註釋
遇到複雜的、比較難理解的正則表達式時,咱們能夠經過添加註釋的方式進行解釋說明。在正則表達式中添加註釋的語法爲:(?#註釋內容)。好比:
@"\((\d{3,4})(?#該分組匹配區號)\)(\d{7,8})(?#該分組匹配電話號碼)"。