.NET正則類及其方法應(yīng)用
1 概述
初學(xué)正則時(shí),對(duì)于Regex類不熟悉,遇到問題不知道該用哪種方法解決,本文結(jié)合一些正則應(yīng)用的典型應(yīng)用場(chǎng)景,介紹一下Regex類的基本應(yīng)用。這里重點(diǎn)進(jìn)行.NET類的介紹,對(duì)于正則的運(yùn)用,不做深入探討。
正則的應(yīng)用最終都是進(jìn)行模式的匹配,而根據(jù)目的的不同,基本上可以分為以下幾種應(yīng)用:驗(yàn)證、提取、替換、分割。結(jié)合.NET提供的控件、類以及類的方法,可以很方便的實(shí)現(xiàn)這些應(yīng)用。
以下將結(jié)合一些典型的應(yīng)用場(chǎng)景,對(duì).NET中常見的類、方法及屬性進(jìn)行介紹。本文旨在.NET類基礎(chǔ)用法的引導(dǎo),對(duì)于其中涉及到的正則表達(dá)式不做深入探討。本文適合于在.NET平臺(tái)下使用正則的初學(xué)者。
2 基礎(chǔ)應(yīng)用
2.1 驗(yàn)證
驗(yàn)證的目的是為了判斷輸入的源字符串是否符合某一規(guī)律或規(guī)則,根據(jù)需求的不同,可能是校驗(yàn)整個(gè)源字符串,也可能只是校驗(yàn)其中一個(gè)子串。
驗(yàn)證在.NET中一般有兩種應(yīng)用,一種是在驗(yàn)證控件RegularExpressionValidator中,另一種是在程序中。
2.1.1 驗(yàn)證控件RegularExpressionValidator
RegularExpressionValidator是.NET自帶的一種客戶端驗(yàn)證控件,通過簡(jiǎn)單的設(shè)置,即可完成對(duì)某控件輸入值的校驗(yàn)。
基本應(yīng)用語法如下:
- <asp:RegularExpressionValidator
- ID="RegularExpressionValidator1"
- runat="server"
- ControlToValidate="TextBox1"
- ErrorMessage="RegularExpressionValidator"
- ValidationExpression="^([1-9][0-9]*|0)(\.[0-9][2])?$">
- </asp:RegularExpressionValidator>
對(duì)于RegularExpressionValidator控件不做過多介紹,只是說明一下使用時(shí)需要注意的幾點(diǎn):
1、 RegularExpressionValidator進(jìn)行的是客戶端驗(yàn)證;
2、 RegularExpressionValidator中正則使用的是JavaScript語法規(guī)則;
3、 RegularExpressionValidator控件不能驗(yàn)證輸入是否為空。
由于RegularExpressionValidator做的是客戶端驗(yàn)證,很容易被跳過,所以在使用RegularExpressionValidator驗(yàn)證的同時(shí),還要做服務(wù)器端驗(yàn)證。
RegularExpressionValidator最終是要生成客戶端的JavaScript代碼進(jìn)行驗(yàn)證的,所以RegularExpressionValidator使用的正則要求符合JavaScript語法規(guī)則,與.NET的幾點(diǎn)區(qū)別:
1、 不支持逆序環(huán)視,也就是(?<=Expression)和(?<!Expression)這樣的語法;
2、 元字符僅支持ASCII碼,即\w等價(jià)于[a-zA-Z0-9_],\d等價(jià)于[0-9]
RegularExpressionValidator控件一般是用來驗(yàn)證某一控件輸入的字符串整體是否符合某一規(guī)則的,所以通常情況下“^”和“$”是必不可少的;在使用“|”表示“或”的關(guān)系時(shí),一定要用“()”來限定“|”作用范圍,比如0-100可以寫作“^([1-9]?[0-9]|100)$”。
RegularExpressionValidator是不能驗(yàn)證輸入是否為空的,驗(yàn)證是否為空要用RequiredFieldValidator控件。
RegularExpressionValidator驗(yàn)證控件是.NET為方便客戶端驗(yàn)證封裝的一組驗(yàn)證控件之一,但由于RegularExpressionValidator受限于支持的正則語法規(guī)則,只能做有限的格式校驗(yàn),一些復(fù)雜的校驗(yàn)可以通過自己寫JavaScript代碼來實(shí)現(xiàn),也是很簡(jiǎn)單的。
2.1.2 程序驗(yàn)證——IsMatch()
程序中的校驗(yàn)基本上就是使用IsMatch方法,驗(yàn)證的對(duì)象可能是源字符串的整體,也可能只是其中一個(gè)子串。
驗(yàn)證源字符串的整體是否符合某一規(guī)則,與使用RegularExpressionValidator時(shí)的需求基本上一致,不過由于是在.NET程序中,所以使用的是.NET的語法,比JavaScript中要強(qiáng)大得多。比如驗(yàn)證一個(gè)文本框輸入的字符串是否符合某一規(guī)則,就是一個(gè)典型的驗(yàn)證整體的需求。
舉例1:驗(yàn)證textBox1輸入內(nèi)容,要求整數(shù)部分為0或正整數(shù),小數(shù)可有可無,有小數(shù)時(shí)必須為2位。
- Regex reg = new Regex(@"^(?:[1-9][0-9]*|0)(?:\.[0-9]{2})?$");
- if (reg.IsMatch(textBox1.Text))
- {
- richTextBox2.Text = "輸入格式正確!";
- }
- else
- {
- richTextBox2.Text = "輸入格式錯(cuò)誤!";
- }
由于是對(duì)源字符串的整體進(jìn)行驗(yàn)證,所以“^”和“$”是必不可少的。否則驗(yàn)證的結(jié)果可能是錯(cuò)誤的,比如正則表達(dá)式“(?:[1-9][0-9]*|0)(?:\.[0-9]{2})?”,在輸入“0.123”時(shí)是可以匹配成功的,匹配結(jié)果為“0.12”,此時(shí)正則只起到了匹配的作用,沒有起到驗(yàn)證的作用。
驗(yàn)證源字符串的局部是否符合某一規(guī)則,就是對(duì)于源字符串中子串的校驗(yàn),通常是用來判斷源字符串中是否包含,或是不包含符合某一規(guī)律的子串,作用類似于string類中的IndexOf。
舉例2(參考問兩個(gè)正則表達(dá)式):
數(shù)據(jù):
1985aaa1985bb
bcae1958fiefadf1955fef
atijc1944cvkd
df2564isdjfef2564d
abc1234def5678ghi5678jkl
需求1:驗(yàn)證字符串中任意位置出現(xiàn)的連續(xù)四個(gè)數(shù)字在整個(gè)字符串中是否有重復(fù),有重復(fù)為True,無重復(fù)為False。
以上數(shù)據(jù)需求1的驗(yàn)證結(jié)果為True的應(yīng)為:
1985aaa1985bb
df2564isdjfef2564d
abc1234def5678ghi5678jkl
因?yàn)樾枨笾兄该魇侨我馕恢玫倪B續(xù)4個(gè)數(shù)字是否有重復(fù),所以在找到重復(fù)前,要遍歷源字符串中每一個(gè)位置時(shí)行驗(yàn)證,這樣就不能限定開始標(biāo)識(shí)符“^”;而在匹配過程中,除非一直到結(jié)尾仍找不到重復(fù),否則只要匹配到有重復(fù)的位置就可以了,這樣也不需要結(jié)束標(biāo)識(shí)符“$”,所以這是典型的對(duì)字符串的子串行驗(yàn)證的需求。
代碼實(shí)現(xiàn):
- string[] test = new string[] { "1985aaa1985bb", "bcae1958fiefadf1955fef",
- "atijc1944cvkd", "df2564isdjfef2564d", "abc1234def5678ghi5678jkl" };
- Regex reg = new Regex(@"(\d{4})(?:(?!\1).)*\1");
- foreach (string s in test)
- {
- richTextBox2.Text += "源字符串: " + s.PadRight(25, ' ') + "驗(yàn)證結(jié)果: " + reg.IsMatch(s) + "\n";
- }
- /*--------輸出--------
- 源字符串: 1985aaa1985bb 驗(yàn)證結(jié)果: True
- 源字符串: bcae1958fiefadf1955fef 驗(yàn)證結(jié)果: False
- 源字符串: atijc1944cvkd 驗(yàn)證結(jié)果: False
- 源字符串: df2564isdjfef2564d 驗(yàn)證結(jié)果: True
- 源字符串: abc1234def5678ghi5678jkl 驗(yàn)證結(jié)果: True*/
由于涉及到了重復(fù)問題,所以這里用到了反向引用,對(duì)于反向引用的細(xì)節(jié),可以參考正則基礎(chǔ)之——反向引用。
需求2:驗(yàn)證字符串中第一個(gè)出現(xiàn)的連續(xù)4個(gè)數(shù)字是否有重復(fù),有重復(fù)為True,無重復(fù)為False。
以上數(shù)據(jù)需求2的驗(yàn)證結(jié)果為True的應(yīng)為:
1985aaa1985bb
df2564isdjfef2564d
因?yàn)樾枨笾兄该魇堑谝粋€(gè)是否有重復(fù),所以需要有開始標(biāo)識(shí)符“^”,來保證是第一個(gè)出現(xiàn)的連續(xù)4個(gè)數(shù)字;而在匹配過程中,除非一直到結(jié)尾仍找不到重復(fù),否則只要匹配到有重復(fù)的位置就可以了,這樣也不需要結(jié)束標(biāo)識(shí)符“$”,所以這仍是對(duì)字符串的子串行驗(yàn)證的需求,只不過相對(duì)于需求1來說,加了一個(gè)限定條件。
代碼實(shí)現(xiàn):
- string[] test = new string[] { "1985aaa1985bb", "bcae1958fiefadf1955fef",
- "atijc1944cvkd", "df2564isdjfef2564d", "abc1234def5678ghi5678jkl" };
- Regex reg = new Regex(@"^(?:(?!\d{4}).)*(\d{4})(?:(?!\1).)*\1");
- foreach (string s in test)
- {
- richTextBox2.Text += "源字符串: " + s.PadRight(25, ' ') + "驗(yàn)證結(jié)果: " + reg.IsMatch(s) + "\n";
- }
- /*--------輸出--------
- 源字符串: 1985aaa1985bb 驗(yàn)證結(jié)果: True
- 源字符串: bcae1958fiefadf1955fef 驗(yàn)證結(jié)果: False
- 源字符串: atijc1944cvkd 驗(yàn)證結(jié)果: False
- 源字符串: df2564isdjfef2564d 驗(yàn)證結(jié)果: True
- 源字符串: abc1234def5678ghi5678jkl 驗(yàn)證結(jié)果: False
- */
2.2 提取——Match()、Matches()
提取主要是從源字符串中,取得一個(gè)或多個(gè)符合某一規(guī)律或規(guī)則的子串。一般來說,在字符串處理中,提取應(yīng)用比較廣泛。提取時(shí)用得比較多的是Match()和Matches()方法,以及結(jié)果處理時(shí)Match類和MatchCollection類的一些方法,有時(shí)也會(huì)用到Capture類的一些方法。
2.2.1 提取單次匹配內(nèi)容——Match()
當(dāng)需要提取的內(nèi)容只有一個(gè),或是只需要獲取第一次成功匹配的內(nèi)容時(shí),可以使用Match()方法。當(dāng)使用Match()方法時(shí),只要在某一位置匹配成功,就不再繼續(xù)嘗試匹配,并返回一個(gè)Match類型的對(duì)象。
舉例:提取姓名
源字符串:姓名:張三,性別:男,年齡:25
代碼實(shí)現(xiàn):
- string test = "姓名:張三,性別:男,年齡:25";
- Regex reg = new Regex(@"(?<=姓名:)[^,]+");
- Match m = reg.Match(test);
- if (m.Success) //驗(yàn)證是否匹配成功
- {
- richTextBox2.Text = m.Value;
- }
- /*--------輸出--------
- 張三
- */
雖然Match()只是取一次匹配,但是可以通過捕獲組來獲取多個(gè)指定子串,比如獲取第一個(gè)<a…>標(biāo)簽的鏈接和文本。
- string test = "abc<a href=\"www.test1.com\">測(cè)試一</a>def<a href=\"www.test2.com\">測(cè)試二</a>ghi";
- Regex reg = new Regex(@"(?is)<a(?:(?!href=).)href=(['""]?)(?<url>[^""\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>");
- Match m = reg.Match(test);
- if(m.Success)
- {
- richTextBox2.Text += m.Groups["url"].Value + "\n"; //鏈接
- richTextBox2.Text += m.Groups["text"].Value + "\n"; //文本
- }
- /*--------輸出--------
- www.test1.com
- 測(cè)試一
- */
對(duì)于捕獲組捕獲結(jié)果的引用,還有一種方式
- string test = "abc<a href=\"www.test1.com\">測(cè)試一</a>def<a href=\"www.test2.com\">測(cè)試二</a>ghi";
- Regex reg = new Regex(@"(?is)<a(?:(?!href=).)href=(['""]?)(?<url>[^""\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>"); Match m = reg.Match(test);
- if(m.Success)
- {
- richTextBox2.Text += m.Result("${url}") + "\n"; //鏈接
- richTextBox2.Text += m.Result("${text}") + "\n"; //文本
- }
- /*--------輸出--------
- www.test1.com
- 測(cè)試一
- */
這兩種方法獲取的結(jié)果都是一樣的,使用哪一種,通常根據(jù)個(gè)人習(xí)慣而定。
2.2.2 提取多次匹配內(nèi)容——Matches()
當(dāng)需要提取的內(nèi)容有多個(gè),并且需要提取所有符合規(guī)律的子串時(shí),可以使用Matches()方法。當(dāng)使用Matches()方法時(shí),需要遍歷源字符串的每一個(gè)位置進(jìn)行嘗試匹配,匹配結(jié)束返回一個(gè)MatchCollection類型的對(duì)象。
對(duì)于1.2.1節(jié)提到的提取鏈接和文本的例子,如果提取的是全部鏈接和文本,而不僅僅是第一個(gè)時(shí),可以使用Matches()方法。
- string test = "abc<a href=\"www.test1.com\">測(cè)試一</a>def<a href=\"www.test2.com\">測(cè)試二</a>ghi";
- Regex reg = new Regex(@"(?is)<a(?:(?!href=).)href=(['""]?)(?<url>[^""\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>");
- MatchCollection mc = reg.Matches(test);
- foreach(Match m in mc)
- {
- richTextBox2.Text += m.Groups["url"].Value + "\n"; //鏈接
- richTextBox2.Text += m.Groups["text"].Value + "\n"; //文本
- }
- /*--------輸出--------
- www.test1.com
- 測(cè)試一
- www.test2.com
- 測(cè)試二
- */
對(duì)于Matches(),某些場(chǎng)景下,也可以通過Count屬性,用做統(tǒng)計(jì)符合某一規(guī)律的子串出現(xiàn)的次數(shù),例如統(tǒng)計(jì)字符串中獨(dú)立的“3”出現(xiàn)的次數(shù)。
- string test = "137,1,33,4,3,6,21,3,35,93,2,98";
- Regex reg = new Regex(@"\b3\b");
- int count = reg.Matches(test).Count; //2
這時(shí)候關(guān)心的只是匹配成功的次數(shù),對(duì)于匹配的內(nèi)容是不關(guān)心的,所以實(shí)現(xiàn)這種需求時(shí),正則應(yīng)盡量簡(jiǎn)潔,能達(dá)到目的即可,這樣可以加快匹配效率,減少資源占用。比如上面的提取鏈接的源字符串中,統(tǒng)計(jì)<a…>標(biāo)簽出現(xiàn)的次數(shù),一般來說,如下代碼即可達(dá)到目的了。
- string test = "abc<a href=\"www.test1.com\">
- 測(cè)試一</a>def<a href=\"www.test2.com\">測(cè)試二</a>ghi";
- Regex reg = new Regex(@"(?i)<a\b");
- int count = reg.Matches(test).Count; //2
2.2.3 捕獲組匹配過程集合——Capture
在某些情況下,一個(gè)正則表達(dá)式整體匹配一次時(shí),其中的捕獲組可能會(huì)匹配多次。
舉例:
源字符串:<region name=oldslist col=1 row=2 order=asc>abcsadf </region> jfdsajf <region name=newslist class=list col=4 row=10 order=desc>abcsadf </region>
需求:提取出每個(gè)region的屬性和屬性值,按region分組。
對(duì)于這個(gè)需求,可以先提取出所有region,再對(duì)每個(gè)region標(biāo)簽提取它的屬性和屬性值,但這樣做比較麻煩,可以考慮在一個(gè)正則表達(dá)式中提取。由于屬性的個(gè)數(shù)是不固定的,所以不能用固定個(gè)數(shù)的量詞來匹配屬性對(duì),正則可以寫為
- (?is)<region\s+(?:(?<key>[^\s=]+)=(?<value>[^\s>]+)\s*)+>
此時(shí)如果還用Groups來取匹配結(jié)果時(shí),由于Groups只保留最后一次匹配結(jié)果,所以只能取到最后一次匹配成功的子串。這時(shí)就要用到Captures屬性。
- string test = "<region name=oldslist col=1 row=2 order=asc>abcsadf
- </region> jfdsajf
- <region name=newslist class=list col=4 row=10 order=desc>abcsadf
- </region>";
- MatchCollection mc = Regex.Matches(test, @"(?is)
- <region\s+(?:(?<key>[^\s=]+)=(?<value>[^\s>]+)\s*)+>");
- for (int i = 0; i < mc.Count; i++)
- {
- richTextBox2.Text += "第" + (i + 1) + "個(gè)region的屬性:\n";
- for (int j = 0; j < mc[i].Groups["key"].Captures.Count; j++)
- {
- richTextBox2.Text += "屬性: " + mc[i].Groups["key"].Captures[j].Value.PadRight(10, ' ')
- + " 值: " + mc[i].Groups["value"].Captures[j].Value + "\n";
- }
- richTextBox2.Text += "\n";
- }
- /*--------輸出--------
第1個(gè)region的屬性:
屬性: name 值: oldslist
屬性: col 值: 1
屬性: row 值: 2
屬性: order 值: asc
第2個(gè)region的屬性:
屬性: name 值: newslist
屬性: class 值: list
屬性: col 值: 4
屬性: row 值: 10
屬性: order 值: desc
*/
Group實(shí)際上是Capture的一個(gè)集合,在捕獲組只匹配一個(gè)子串時(shí),這個(gè)集合只有一個(gè)元素,而在捕獲組先后匹配多個(gè)子串時(shí),Groups[i].Value只保留最后一個(gè)匹配結(jié)果,而Capture集合卻可以記錄匹配過程中匹配到的所有子串。
Capture的應(yīng)用場(chǎng)景并不多,對(duì)于上面的例子,如果不使用 Capture,可以通過分次匹配的方式實(shí)現(xiàn),但是在一些復(fù)雜的表達(dá)式中,很難進(jìn)行分次匹配,這時(shí)Capture就比較有用了。
2.3 替換
替換主要是從源字符串中,將符合某一規(guī)律或規(guī)則的子串替換為其它內(nèi)容。一般來說,在字符串處理中,替換應(yīng)用也比較廣泛。替換主要是用Replace()方法,在一些替換規(guī)則復(fù)雜的應(yīng)用場(chǎng)景中,也可能會(huì)用到委托方法。
2.3.1 一般替換
替換的目的很明確,只要找出待替換子串的規(guī)律,替換為目標(biāo)子串就可以了。
舉例1:
源字符串:<img src="/imgs/logo.jpg"> abc <img src="http://www.test.com/imgs/top.gif">
需求:將相對(duì)地址替換為絕對(duì)地址,已經(jīng)是絕對(duì)地址的不替換。
- string test = "<img src=\"/imgs/logo.jpg\"> abc <img src=\"http://www.test.com/imgs/top.gif\">";
- Regex reg = new Regex(@"(?i)(?<=src=(['""]?))(?!http://)(?=[^'""\s>]+\1)");
- string result = reg.Replace(test, "http://www.test.com");
- /*--------輸出--------
- <img src="http://www.test.com/imgs/logo.jpg"> abc <img src="http://www.test.com/imgs/top.gif">
- */
這里需要說明的是,在.NET中只提供了一個(gè)Replace()方法,沒有提供類似于Java中的replaceAll()和replaceFirst()兩種方法,所以如果在.NET中只替換第一次出現(xiàn)的符合規(guī)律的子串時(shí),需要在正則表達(dá)式中處理。
舉例2:
源字符串:abc123def123ghi
需求:將第一次出現(xiàn)的“123”替換為空,其余位置不替換。
- string test = "abc123def123ghi";
- Regex reg = new Regex(@"(?s)^((?:(?!123).)*)123");
- string result = reg.Replace(test, "$1");
- /*--------輸出--------
- abcdef123ghi
- */
這時(shí)用“^”限定只替換第一次出現(xiàn)的子串,由于絕大多數(shù)正則引擎都對(duì)“^”進(jìn)行了優(yōu)化,所以正則表達(dá)式在位置0處匹配成功或失敗后,將不再對(duì)其它位置進(jìn)行嘗試匹配。
2.3.2 替換中的委托方法應(yīng)用
對(duì)于一些比較復(fù)雜的替換規(guī)則,可能會(huì)用到委托方法,由于這種應(yīng)用屬于比較典型的一種應(yīng)用,所以將在后面的文章中單獨(dú)進(jìn)行介紹
2.4 分割
分割就是用符合某一規(guī)律的子串,將源字符串分割成一個(gè)數(shù)組,主要用到的是Split()方法。由于Regex的Split()方法中,并沒有提供類似于string的Split()方法的StringSplitOptions.RemoveEmptyEntries參數(shù),而如果符合規(guī)律的子串出現(xiàn)在開頭或結(jié)尾時(shí),會(huì)出現(xiàn)不需要的空串,這時(shí)需要在正則表達(dá)式中進(jìn)行一下處理。
舉例1:
源字符串:漢字123文字english
需求:按英文單詞和非英文單詞進(jìn)行分割(英文單詞包括由a-z,A-Z,0-9,_組成的子串)。
- string str = "漢字123文字english";
- string[] result = Regex.Split(str, @"(?<!^)\b(?!$)", RegexOptions.ECMAScript);
- foreach (string s in result)
- {
- richTextBox2.Text += s + "\n";
- }
- /*--------輸出--------
- 漢字
- 123
- 文字
- English
- */
這里分別用“(?<!^)”和“(?!$)”來限定不以開頭或結(jié)尾的子串進(jìn)行分割,結(jié)果中也就不會(huì)出現(xiàn)不必要的空串了。
還有一些應(yīng)用,其實(shí)可以算作是正則就用技巧范疇的了。
舉例2:
源字符串:Left(姓名,1),Left(姓名,1) , Left (姓名,1)
需求:以不在“()”內(nèi)的“,”進(jìn)行分割。
- string test = "Left(姓名,1),Left(姓名,1) , Left (姓名,1)";
- Regex reg = new Regex(@"(?<!\([^)]*),(?![^(]*\))");
- string[] sArray = reg.Split(test);
- foreach (string s in sArray)
- {
- richTextBox2.Text += s + "\n";
- }
- /*--------輸出--------
- Left(姓名,1)
- Left(姓名,1)
- Left (姓名,1)
- */
使用正則的Split()方法時(shí),有一點(diǎn)需求注意,那就是如果正則中出現(xiàn)了捕獲組,那么捕獲組匹配到的內(nèi)容也會(huì)保存到分割結(jié)果中。
以下舉例不做詳細(xì)說明,看下結(jié)果基本上就明白了。
- string test = "aa11<bbb>cc22<ddd>ee";
- string[] temp = Regex.Split(test, @"[0-9]+(<[^>]*>)");
- foreach (string s in temp)
- {
- richTextBox2.Text += s + "\n";
- }
- /*--------輸出--------
- aa
- <bbb>
- cc
- <ddd>
- ee
- */
如果不想把捕獲組匹配到的內(nèi)容也存入結(jié)果,可以使用非捕獲組。
- string test = "aa11<bbb>cc22<ddd>ee";
- string[] temp = Regex.Split(test, @"[0-9]+(?:<[^>]*>)");
- foreach (string s in temp)
- {
- richTextBox2.Text += s + "\n";
- }
- /*--------輸出--------
- aa
- cc
- ee
- */
3 擴(kuò)展應(yīng)用
這里介紹一些可能涉及到的一些.NET中的正則擴(kuò)展應(yīng)用。
3.1 動(dòng)態(tài)生成正則時(shí)的轉(zhuǎn)義——Escape()
有時(shí)需要根據(jù)一些變量動(dòng)態(tài)生成正則表達(dá)式,這時(shí)如果變量中含有正則中的元字符,會(huì)被解析成元字符,就可能會(huì)導(dǎo)致正則編譯不通過,從而導(dǎo)致程序異常,需要對(duì)變量進(jìn)行轉(zhuǎn)義處理。Regex. Escape()方法通過替換為轉(zhuǎn)義碼來轉(zhuǎn)義最小的字符集(\、*、+、?、|、{、[、(、)、^、$、.、# 和空白)。
比如根據(jù)用戶輸入的id取相應(yīng)的div標(biāo)簽,id中沒有元字符時(shí),可以取得正確結(jié)果。
- string test = "<div id=\"test1\">abc</div><div id=\"test2\">def</div>";
- string[] ids = new string[] { "test1", "test2" };
- foreach (string id in ids)
- {
- Regex reg = new Regex(@"(?is)<div\s+id=""" + id + @"""[^>]*>(?:(?!</?div\b).)*</div>");
- MatchCollection mc = reg.Matches(test);
- foreach (Match m in mc)
- {
- richTextBox2.Text += m.Value + "\n";
- }
- }
- /*--------輸出--------
- <div id="test1">abc</div>
- <div id="test2">def</div>
- */
但是如果輸入的id中一旦出現(xiàn)未經(jīng)轉(zhuǎn)義的元字符,如“abc(”,就會(huì)拋類似于下面的異常。
正在分析“(?is)<div\s+id="abc("[^>]*>(?:(?!</?div\b).)*</div>”- ) 不足。
此時(shí)可以用Escape()方法對(duì)輸入的變量進(jìn)行轉(zhuǎn)義處理。
- string test = "<div id=\"test1\">abc</div><div id=\"test2\">def</div>";
- string[] ids = new string[] { "test1", "test2", "abc(" };
- foreach (string id in ids)
- {
- Regex reg = new Regex(@"(?is)<div\s+id=""" + Regex.Escape(id) + @"""[^>]*>(?:(?!</?div\b).)*</div>");
- MatchCollection mc = reg.Matches(test);
- foreach (Match m in mc)
- {
- richTextBox2.Text += m.Value + "\n";
- }
- }
- /*--------輸出--------
- <div id="test1">abc</div>
- <div id="test2">def</div>
- */
使用Escape()方法轉(zhuǎn)義后,就可以得到正確的結(jié)果,而不會(huì)拋異常了。
3.2 靜態(tài)方法
.NET中一些Regex類的常見方法都提供了相應(yīng)的靜態(tài)方法,可以不顯式的聲明Regex對(duì)象,而直接調(diào)用相應(yīng)的方法,書寫起來更方便,代碼更簡(jiǎn)潔、易讀。
比如替換IP地址最后一節(jié)為“*”,只需一行代碼。
string result = Regex.Replace("10.27.123.12", @"\d+$", "*"); //10.27.123.*
靜態(tài)方法每次調(diào)用都會(huì)創(chuàng)建一個(gè)臨時(shí)的Regex對(duì)象,使用之后釋放,所以每次調(diào)用靜態(tài)方法時(shí),都會(huì)重新編譯,而這將會(huì)降低執(zhí)行效率。因此在循環(huán)或是頻繁調(diào)用的方法中,不適合使用靜態(tài)方法,而需要進(jìn)行顯式聲明Regex對(duì)象。
但是對(duì)于一些只調(diào)用一次,或是對(duì)執(zhí)行效率沒有要求的場(chǎng)景下,靜態(tài)方法則是很不錯(cuò)的選擇。
本文來自CSDN博客:http://blog.csdn.net/lxcnn/archive/2009/10/12/4656599.aspx
【編輯推薦】
- 什么是正則表達(dá)式:其歷史與.NET框架的關(guān)系
- 詳解C#正則表達(dá)式
- 利用正則表達(dá)式使C#判斷輸入日期格式是否正確
- .NET下正則表達(dá)式應(yīng)用四例
- JAVA正則表達(dá)式4種常用的功能