代碼 | 說明 |
. | 匹配除換行符以外的任意字符。 |
\w | 匹配字母或數字或下劃線或漢字。 |
\s | 匹配任意的空白符。 |
\d | 匹配數字。 |
\b | 匹配單詞的開始或結束。 |
[ck] | 匹配包含括號內元素的字符 |
^ | 匹配行的開始。 |
$ | 匹配行的結束。 |
\ | 對下一個字符轉義。比如$是個特殊的字符。要匹配$的話就得用\$ |
| | 分支條件,如:x|y匹配 x 或 y。 |
反義元字符
代碼 | 說明 |
\W | 匹配任意不是字母,數字,下劃線,漢字的字符。 |
\S | 匹配任意不是空白符的字符。等價于 [^ \f\n\r\t\v]。 |
\D | 匹配任意非數字的字符。等價于 [^0-9]。 |
\B | 匹配不是單詞開頭或結束的位置。 |
[^CK] | 匹配除了CK以外的任意字符。 |
特殊元字符
代碼 | 說明 |
\f | 匹配一個換頁符。等價于 \x0c 和 \cL。 |
\n | 匹配一個換行符。等價于 \x0a 和 \cJ。 |
\r | 匹配一個回車符。等價于 \x0d 和 \cM。 |
\t | 匹配一個制表符。等價于 \x09 和 \cI。 |
\v | 匹配一個垂直制表符。等價于 \x0b 和 \cK。 |
限定符
代碼 | 說明 |
* | 匹配前面的子表達式零次或多次。 |
+ | 匹配前面的子表達式一次或多次。 |
? | 匹配前面的子表達式零次或一次。 |
{n} | n 是一個非負整數。匹配確定的 n 次。 |
{n,} | n 是一個非負整數。至少匹配n 次。 |
{n,m} | m 和 n 均為非負整數,其中n = m。最少匹配 n 次且最多匹配 m 次。 |
懶惰限定符
代碼 | 說明 |
*? |
重復任意次,但盡可能少重復。 如 "acbacb" 正則 "a.*?b" 只會取到第一個"acb" 原本可以全部取到但加了限定符后,只會匹配盡可能少的字符 ,而"acbacb"最少字符的結果就是"acb" 。 |
+? | 重復1次或更多次,但盡可能少重復。與上面一樣,只是至少要重復1次。 |
?? |
重復0次或1次,但盡可能少重復。 如 "aaacb" 正則 "a.??b" 只會取到最后的三個字符"acb"。 |
{n,m}? |
重復n到m次,但盡可能少重復。 如 "aaaaaaaa" 正則 "a{0,m}" 因為最少是0次所以取到結果為空。 |
{n,}? |
重復n次以上,但盡可能少重復。 如 "aaaaaaa" 正則 "a{1,}" 最少是1次所以取到結果為 "a"。 |
捕獲分組
代碼 | 說明 |
(exp) | 匹配exp,并捕獲文本到自動命名的組里。 |
(?name>exp) | 匹配exp,并捕獲文本到名稱為name的組里。 |
(?:exp) | 匹配exp,不捕獲匹配的文本,也不給此分組分配組號以下為零寬斷言。 |
(?=exp) |
匹配exp前面的位置。 如 "How are you doing" 正則"(?txt>.+(?=ing))" 這里取ing前所有的字符,并定義了一個捕獲分組名字為 "txt" 而"txt"這個組里的值為"How are you do"; |
(?=exp) |
匹配exp后面的位置。 如 "How are you doing" 正則"(?txt>(?=How).+)" 這里取"How"之后所有的字符,并定義了一個捕獲分組名字為 "txt" 而"txt"這個組里的值為" are you doing"; |
(?!exp) |
匹配后面跟的不是exp的位置。 如 "123abc" 正則 "\d{3}(?!\d)"匹配3位數字后非數字的結果 |
(?!exp) |
匹配前面不是exp的位置。 如 "abc123 " 正則 "(?![0-9])123" 匹配"123"前面是非數字的結果也可寫成"(?!\d)123" |
得到上面秘籍后我們可以小試牛刀...
小試牛刀
在C#中使用正則表達式主要是通過Regex類來實現。命名空間:using System.Text.RegularExpressions。
其中常用方法:
名稱 | 說明 |
IsMatch(String, String) | 指示 Regex 構造函數中指定的正則表達式在指定的輸入字符串中是否找到了匹配項。 |
Match(String, String) | 在指定的輸入字符串中搜索 Regex 構造函數中指定的正則表達式的第一個匹配項。 |
Matches(String, String) | 在指定的輸入字符串中搜索正則表達式的所有匹配項。 |
Replace(String, String) | 在指定的輸入字符串內,使用指定的替換字符串替換與某個正則表達式模式匹配的所有字符串。 |
Split(String, String) | 在由 Regex 構造函數指定的正則表達式模式所定義的位置,拆分指定的輸入字符串。 |
在使用正則表達式前我們先來看看“@”符號的使用。
學過C#的人都知道C# 中字符串常量可以以@ 開頭聲名,這樣的優點是轉義序列“不”被處理,按“原樣”輸出,即我們不需要對轉義字符加上 \ (反斜扛),就可以輕松coding。如:
如要在一個用 @ 引起來的字符串中包括一個雙引號,就需要使用兩對雙引號了。這時候你不能使用 \ 來轉義爽引號了,因為在這里 \ 的轉義用途已經被 @ “屏蔽”掉了。如:
字符串匹配:
在實際項目中我們常常需要對用戶輸入的信息進行驗證。如:匹配用戶輸入的內容是否為數字,是否為有效的手機號碼,郵箱是否合法....等。
實例代碼:
string RegexStr = string.Empty; #region 字符串匹配 RegexStr = "^[0-9]+$"; //匹配字符串的開始和結束是否為0-9的數字[定位字符] Console.WriteLine("判斷'R1123'是否為數字:{0}", Regex.IsMatch("R1123", RegexStr)); Console.WriteLine("判斷'1123'是否為數字:{0}", Regex.IsMatch("1123", RegexStr)); RegexStr = @"\d+"; //匹配字符串中間是否包含數字(這里沒有從開始進行匹配噢,任意位子只要有一個數字即可) Console.WriteLine("'R1123'是否包含數字:{0}", Regex.IsMatch("R1123", RegexStr)); Console.WriteLine("'博客園'是否包含數字:{0}", Regex.IsMatch("博客園", RegexStr)); //感謝@zhoumy的提醒..已修改錯誤代碼 RegexStr = @"^Hello World[\w\W]*"; //已Hello World開頭的任意字符(\w\W:組合可匹配任意字符) Console.WriteLine("'HeLLO WORLD xx hh xx'是否已Hello World開頭:{0}", Regex.IsMatch("HeLLO WORLD xx hh xx", RegexStr, RegexOptions.IgnoreCase)); Console.WriteLine("'LLO WORLD xx hh xx'是否已Hello World開頭:{0}", Regex.IsMatch("LLO WORLD xx hh xx", RegexStr,RegexOptions.IgnoreCase)); //RegexOptions.IgnoreCase:指定不區分大小寫的匹配。 #endregion
顯示結果:
字符串查找:
實例代碼:
string RegexStr = string.Empty; #region 字符串查找 string LinkA = "a href=\"http://www.baidu.com\" target=\"_blank\">百度/a>"; RegexStr = @"href=""[\S]+"""; // ""匹配" Match mt = Regex.Match(LinkA, RegexStr); Console.WriteLine("{0}。", LinkA); Console.WriteLine("獲得href中的值:{0}。", mt.Value); RegexStr = @"h[^23456]>[\S]+h[1]>"; //h[^23456]>:匹配h除了2,3,4,5,6之中的值,h[1]>:h匹配包含括號內元素的字符 Console.WriteLine("{0}。GetH1值:{1}", "H1>標題H1>", Regex.Match("H1>標題H1>", RegexStr, RegexOptions.IgnoreCase).Value); Console.WriteLine("{0}。GetH1值:{1}", "h2>小標h2>", Regex.Match("h2>小標h2>", RegexStr, RegexOptions.IgnoreCase).Value); //RegexOptions.IgnoreCase:指定不區分大小寫的匹配。 RegexStr = @"ab\w+|ij\w{1,}"; //匹配ab和字母 或 ij和字母 Console.WriteLine("{0}。多選結構:{1}", "abcd", Regex.Match("abcd", RegexStr).Value); Console.WriteLine("{0}。多選結構:{1}", "efgh", Regex.Match("efgh", RegexStr).Value); Console.WriteLine("{0}。多選結構:{1}", "ijk", Regex.Match("ijk", RegexStr).Value); RegexStr = @"張三?豐"; //?匹配前面的子表達式零次或一次。 Console.WriteLine("{0}。可選項元素:{1}", "張三豐", Regex.Match("張三豐", RegexStr).Value); Console.WriteLine("{0}。可選項元素:{1}", "張豐", Regex.Match("張豐", RegexStr).Value); Console.WriteLine("{0}。可選項元素:{1}", "張飛", Regex.Match("張飛", RegexStr).Value); /* 例如: July|Jul 可縮短為 July? 4th|4 可縮短為 4(th)? */ //匹配特殊字符 RegexStr = @"Asp\.net"; //匹配Asp.net字符,因為.是元字符他會匹配除換行符以外的任意字符。這里我們只需要他匹配.字符即可。所以需要轉義\.這樣表示匹配.字符 Console.WriteLine("{0}。匹配Asp.net字符:{1}", "Java Asp.net SQLServer", Regex.Match("Java Asp.net SQLServer", RegexStr).Value); Console.WriteLine("{0}。匹配Asp.net字符:{1}", "C# Java", Regex.Match("C# Java", RegexStr).Value); #endregion
顯示結果:
貪婪與懶惰
string f = "fooot"; //貪婪匹配 RegexStr = @"f[o]+"; Match m1 = Regex.Match(f, RegexStr); Console.WriteLine("{0}貪婪匹配(匹配盡可能多的字符):{1}", f, m1.ToString()); //懶惰匹配 RegexStr = @"f[o]+?"; Match m2 = Regex.Match(f, RegexStr); Console.WriteLine("{0}懶惰匹配(匹配盡可能少重復):{1}", f, m2.ToString());
顯示結果:
從上面的例子中我們不難看出貪婪與懶惰的區別,他們的名子取的都很形象。
貪婪匹配:匹配盡可能多的字符。
懶惰匹配:匹配盡可能少的字符。
(exp)分組
在做爬蟲時我們經常獲得A中一些有用信息。如href,title和顯示內容等。
string TaobaoLink = "a href=\"http://www.taobao.com\" title=\"淘寶網 - 淘!我喜歡\" target=\"_blank\">淘寶/a>"; RegexStr = @"a[^>]+href=""(\S+)""[^>]+title=""([\s\S]+?)""[^>]+>(\S+)/a>"; Match mat = Regex.Match(TaobaoLink, RegexStr); for (int i = 0; i mat.Groups.Count; i++) { Console.WriteLine("第"+i+"組:"+mat.Groups[i].Value); }
顯示結果:
在正則表達式里使用()包含的文本自動會命名為一個組。上面的表達式中共使用了4個()可以認為是分為了4組。
輸出結果共分為:4組。
0組:為我們所匹配的字符串。
1組:是我們第一個括號[href=""(\S+)""]中(\S+)所匹配的網址信息。內容為:http://www.taobao.com。
2組:是第二個括號[title=""([\s\S]+?)""]中所匹配的內容信息。內容為:淘寶網 - 淘!我喜歡。
這里我們會看到+?懶惰限定符。title=""([\s\S]+?)"" 這里+?的下一個字符為"雙引號,"雙引號在匹配字符串后面還有三個。+?懶惰限定符會盡可能少重復,所他會匹配最前面那個"雙引號。如果我們不使用+?懶惰限定符他會匹配到:淘寶網 - 淘!我喜歡" target= 會盡可能多重復匹配。
3組:是第三個括號[(\S+)]所匹配的內容信息。內容為:淘寶。
說明:反義元字符所對應的元字符都能組合匹配任意字符。如:[\w\W],[\s\S],[\d\D]..
(?name>exp) 分組取名
當我們匹配分組信息過多后,在某種場合只需取當中某幾組信息。這時我們可以對分組取名。通過分組名稱來快速提取對應信息。
string Resume = "基本信息姓名:CK|求職意向:.NET軟件工程師|性別:男|學歷:本專|出生日期:1988-08-08|戶籍:湖北.孝感|E - Mail:9245162@qq.com|手機:15000000000"; RegexStr = @"姓名:(?name>[\S]+)\|\S+性別:(?sex>[\S]{1})\|學歷:(?xueli>[\S]{1,10})\|出生日期:(?Birth>[\S]{10})\|[\s\S]+手機:(?phone>[\d]{11})"; Match matc = Regex.Match(Resume, RegexStr); Console.WriteLine("姓名:{0},手機號:{1}", matc.Groups["name"].ToString(), matc.Groups["phone"].ToString());
顯示結果:
通過(?name>exp)可以很輕易為分組取名。然后通過Groups["name"]取得分組值。
獲得頁面中A標簽中href值
string PageInfo = @"hteml> div id=""div1""> a href=""http://www.baidu.con"" target=""_blank"">百度/a> a href=""http://www.taobao.con"" target=""_blank"">淘寶/a> a href=""http://www.cnblogs.com"" target=""_blank"">博客園/a> a href=""http://www.google.con"" target=""_blank"">google/a> /div> div id=""div2""> a href=""/zufang/"">整租/a> a href=""/hezu/"">合租/a> a href=""/qiuzu/"">求租/a> a href=""/ershoufang/"">二手房/a> a href=""/shangpucz/"">商鋪出租/a> /div> /hteml>"; RegexStr = @"a[^>]+href=""(?href>[\S]+?)""[^>]*>(?text>[\S]+?)/a>"; MatchCollection mc = Regex.Matches(PageInfo, RegexStr); foreach (Match item in mc) { Console.WriteLine("href:{0}--->text:{1}",item.Groups["href"].ToString(),item.Groups["text"].ToString()); }
顯示結果:
Replace 替換字符串
用戶在輸入信息時偶爾會包含一些敏感詞,這時我們需要替換這個敏感詞。
string PageInputStr = "靠.TMMD,今天真不爽...."; RegexStr = @"靠|TMMD|媽的"; Regex rep_regex = new Regex(RegexStr); Console.WriteLine("用戶輸入信息:{0}", PageInputStr); Console.WriteLine("頁面顯示信息:{0}", rep_regex.Replace(PageInputStr, "***"));
顯示結果:
對一些敏感詞直接替換成***代替。
Split 拆分字符串
string SplitInputStr = "1xxxxx.2ooooo.3eeee.4kkkkkk."; RegexStr = @"\d"; Regex spl_regex = new Regex(RegexStr); string[] str = spl_regex.Split(SplitInputStr); foreach (string item in str) { Console.WriteLine(item); }
顯示結果:
根據數字截取字符串。
首先,我們先看幾個實際的例子:
1. 驗證輸入字符是否
javascript:
var ex = "^\\w+$";
var re = new RegExp(ex,"i");
return re.test(str);
VBScript
Dim regEx,flag,ex
ex = "^\w+$"
Set regEx = New RegExp
regEx.IgnoreCase = True
regEx.Global = True
regEx.Pattern = ex
flag = regEx.Test( str )
C#
System.String ex = @"^\w+$";
System.Text.RegularExpressions.Regex reg = new Regex( ex );
bool flag = reg.IsMatch( str );
2. 驗證郵件格式
C#
System.String ex = @"^\w+@\w+\.\w+$";
System.Text.RegularExpressions.Regex reg = new Regex( ex );
bool flag = reg.IsMatch( str );
3. 更改日期的格式(用 dd-mm-yy 的日期形式代替 mm/dd/yy 的日期形式)
C#
String MDYToDMY(String input)
{
return Regex.Replace(input,
"\\b(?\\d{1,2})/(?\\d{1,2})/(?\\d{2,4})\\b",
"${day}-${month}-${year}");
}
4. 從 URL 提取協議和端口號
C#
String Extension(String url)
{
Regex r = new Regex(@"^(?\w+)://[^/]+?(?:\d+)?/",
RegexOptions.Compiled);
return r.Match(url).Result("${proto}${port}");
}
這里的例子可能是我們在網頁開發中,通常會碰到的一些正則表達式,尤其在第一個例子中,給出了使用javascript,vbScript,C#等不同語言的實現方式,大家不難看出,對于不同的語言來說,正則表達式沒有區別,只是正則表達式的實現類不同而已。而如何發揮正則表達式的公用,也要看實現類的支持。
(摘自msdn: Microsoft .NET 框架 SDK 提供大量的正則表達式工具,使您能夠高效地創建、比較和修改字符串,以及迅速地分析大量文本和數據以搜索、移除和替換文本模式。ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconregularexpressionslanguageelements.htm)
下面我們逐個來分析這些例子:
1-2,這兩個例子很簡單,只是簡單的驗證字符串是否符合正則表達式規定的格式,其中使用的語法,在第一篇文章中都已經介紹過了,這里做一下簡單的描述。
第1個例子的表達式: ^\w+$
^ -- 表示限定匹配開始于字符串的開始
\w – 表示匹配英文字符
+ -- 表示匹配字符出現1次或多次
$ -- 表示匹配字符到字符串結尾處結束
驗證形如asgasdfs的字符串
第2個例子的表達式: ^\w+@\w+.\w+$
^ -- 表示限定匹配開始于字符串的開始
\w – 表示匹配英文字符
+ -- 表示匹配字符出現1次或多次
@ -- 匹配普通字符@
\. – 匹配普通字符.(注意.為特殊字符,因此要加上\轉譯)
$ -- 表示匹配字符到字符串結尾處結束
驗證形如dragontt@sina.com的郵件格式
第3 個例子中,使用了替換,因此,我們還是先來看看正則表達式中替換的定義:
(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpconsubstitutions.htm)
替換
字符
含義
$123
替換由組號 123(十進制)匹配的最后一個子字符串。
${name}
替換由 (? ) 組匹配的最后一個子字符串。
$$
替換單個“$”字符。
$
替換完全匹配本身的一個副本。
$`
替換匹配前的輸入字符串的所有文本。
$'
替換匹配后的輸入字符串的所有文本。
$+
替換最后捕獲的組。
$_
替換整個輸入字符串。
分組構造
(ms-help://MS.VSCC/MS.MSDNVS.2052/cpgenref/html/cpcongroupingconstructs.htm)
分組構造
定義
( )
捕獲匹配的子字符串(或非捕獲組;有關更多信息,請參閱正則表達式選項中的 ExplicitCapture 選項。)使用 () 的捕獲根據左括號的順序從 1 開始自動編號。捕獲元素編號為零的第一個捕獲是由整個正則表達式模式匹配的文本。
(?name> )
將匹配的子字符串捕獲到一個組名稱或編號名稱中。用于 name 的字符串不能包含任何標點符號,并且不能以數字開頭。可以使用單引號替代尖括號,例如 (?'name')。
(?name1-name2> )
平衡組定義。刪除先前定義的 name2 組的定義并在 name1 組中存儲先前定義的 name2 組和當前組之間的間隔。如果未定義 name2 組,則匹配將回溯。由于刪除 name2 的最后一個定義會顯示 name2 的先前定義,因此該構造允許將 name2 組的捕獲堆棧用作計數器以跟蹤嵌套構造(如括號)。在此構造中,name1 是可選的。可以使用單引號替代尖括號,例如 (?'name1-name2')。
(?: )
非捕獲組。
(?imnsx-imnsx: )
應用或禁用子表達式中指定的選項。例如,(?i-s: ) 將打開不區分大小寫并禁用單行模式。有關更多信息,請參閱正則表達式選項。
(?= )
零寬度正預測先行斷言。僅當子表達式在此位置的右側匹配時才繼續匹配。例如,\w+(?=\d) 與后跟數字的單詞匹配,而不與該數字匹配。此構造不會回溯。
(?! )
零寬度負預測先行斷言。僅當子表達式不在此位置的右側匹配時才繼續匹配。例如,\b(?!un)\w+\b 與不以 un 開頭的單詞匹配。
(?= )
零寬度正回顧后發斷言。僅當子表達式在此位置的左側匹配時才繼續匹配。例如,(?=19)99 與跟在 19 后面的 99 的實例匹配。此構造不會回溯。
(?
零寬度負回顧后發斷言。僅當子表達式不在此位置的左側匹配時才繼續匹配。
(?> )
非回溯子表達式(也稱為貪婪子表達式)。該子表達式僅完全匹配一次,然后就不會逐段參與回溯了。(也就是說,該子表達式僅與可由該子表達式單獨匹配的字符串匹配。)
我們還是先簡單的了解一下這兩個概念:
分組構造:
最基本的構造方式就是(),在左右括號中括起來的部分,就是一個分組;
更進一步的分組就是形如:(?name> )的分組方式,這種方式與第一種方式的不同點,就是對分組的部分進行了命名,這樣就可以通過該組的命名來獲取信息;
(還有形如(?= )等等的分組構造,我們這篇的例子中也沒有使用到,下次我們在來介紹)
替換:
上面提到了兩種基本的構造分組方式()以及(?name> ),通過這兩種分組方式,我們可以得到形如$1,${name}的匹配結果。
這樣說,可能概念上還是有些模糊,我們還是結合上面的例子來說:
第三個例子的正則表達式為:\\b(?\\d{1,2})/(?\\d{1,2})/(?\\d{2,4})\\b
(解釋一下,為什么這里都是\\一起用:這里是C#的例子,在C#語言中\是轉譯字符,要想字符串中的\不轉譯,就需要使用\\或者在整個字符串的開始加上@標記,即上面等價與
@”\b(?\d{1,2})/(?\d{1,2})/(?\d{2,4}\b”)
\b -- 是一種特殊情況。在正則表達式中,除了在 [] 字符類中表示退格符以外,\b 表示字邊界(在 \w 和 \W 字符之間)。在替換模式中,\b 始終表示退格符
(?\d{1,2}) – 構造一個名為month的分組,這個分組匹配一個長度為1-2的數字
/ -- 匹配普通的/字符
(?\d{1,2}) --構造一個名為day的分組,這個分組匹配一個長度為1-2的數字
/ -- 匹配普通的/字符
(?\d{2,4}\b”) --構造一個名為year的分組,這個分組匹配一個長度為2-4的數字
這里還不能夠看出這些分組的作用,我們接著看這一句
${day}-${month}-${year}
${day} – 獲得上面構造的名為day的分組匹配后的信息
- -- 普通的-字符
${month} --獲得上面構造的名為month的分組匹配后的信息
- -- 普通的-字符
${year} --獲得上面構造的名為year的分組匹配后的信息
舉例來說:
將形如04/02/2003的日期使用例3種的方法替換
(?\d{1,2}) 分組將匹配到04由${month}得到這個匹配值
(?\d{1,2}) 分組將匹配到02由${day}得到這個匹配值
(?\d{1,2}) 分組將匹配到2003由${year}得到這個匹配值
了解了這個例子后,我們在來看第4個例子就很簡單了。
第4個例子的正則
^(?\w+)://[^/]+?(?:\d+)?/
^ -- 表示限定匹配開始于字符串的開始
(?\w+) – 構造一個名為proto的分組,匹配一個或多個字母
: -- 普通的:字符
// -- 匹配兩個/字符
[^/] – 表示這里不允許是/字符
+? – 表示指定盡可能少地使用重復但至少使用一次匹配
(?:\d+) – 構造一個名為port的分組,匹配形如:2134(冒號+一個或多個數字)
? – 表示匹配字符出現0次或1次
/ -- 匹配/字符
最后通過${proto}${port}來獲取兩個分組構造的匹配內容
(有關Regex對象的用法,參考
ms-help://MS.VSCC/MS.MSDNVS.2052/cpref/html/frlrfSystemTextRegularExpressionsRegexMembersTopic.htm)
下一篇:驗證注冊用戶名必須為字母