.NET正則基礎(chǔ)之正則委托
1、概述
一般的正則替換,只能對匹配的子串做簡單的處理,且只能是做string類型的處理之后,作為替換文本替換匹配子串,可以實現(xiàn)的功能比較有限。.NET的Replace()方法中的replacement參數(shù),不僅可以是字符串,還可以是委托(delegate),在每次匹配成功時,都會調(diào)用委托方法,對匹配的子串進行處理之后,再作為替換文本返回,匹配子串使用委托方法,可以做任意復(fù)雜的處理,因此這種替換功能非常強大。
委托的類型可以是MatchEvaluator,也可以是匿名方法,在每次匹配成功時調(diào)用。委托方法傳入?yún)?shù)是Match對象,返回類型是string,即正則表達式在每次匹配成功時,會得到一個Match對象,作為參數(shù)傳給委托方法,做一定處理后,返回替換文本,替換匹配到的子串。
2、委托和匿名方法
在正則替換中使用的委托,一般有兩種方式,顯式聲明的委托和匿名方法。下面以實例說明兩種方式的使用方法。委托和匿名方法的區(qū)別和各自的特點不在這里介紹,請參考相關(guān)文獻或文章。
2.1委托
舉例:
源字符串: a=10, b=20, c=30
需求:將字符串中的數(shù)字加100。
//委托方法 private string regReplace(Match m) { return (Convert.ToInt32(m.Value) + 100).ToString(); } //聲明一個MatchEvaluator類型委托 MatchEvaluator me = new MatchEvaluator(regReplace); //正則替換應(yīng)用 string test = "a=10, b=20, c=30"; Regex reg = new Regex(@"(?i)(?<=[a-z]=)\d+"); string result = reg.Replace(test, me); richTextBox2.Text = result; /*--------輸出-------- a=110, b=120, c=130 */
2.2匿名方法
事實上,對于以上這種簡單的需求,不需要顯式的聲明委托,直接使用匿名方法即可,
string test = "a=10, b=20, c=30"; Regex reg = new Regex(@"(?i)(?<=[a-z]=)\d+"); string result = reg.Replace(test, delegate(Match m) { return (Convert.ToInt32(m.Value) + 100).ToString(); }); richTextBox2.Text = result; /*--------輸出-------- a=110, b=120, c=130 */
3、正則中委托的典型應(yīng)用場景
一個涉及到替換的需求,首先要進行分析,是否能夠通過一個正則表達式進行直接替換,如果不可以,那就要借助委托了。接下來就要找出可在委托方法中進行處理的子串的規(guī)律,剩下的就是委托方法中最基本的字符串處理了。
正則中委托的典型應(yīng)用場景一般可歸納為以下幾種:
1、替換子串需進行非string類型的處理,如計算等;
2、替換子串需經(jīng)過條件或邏輯判斷來決定處理方式;
3、多種條件組合的替換。
以上分類方式或許有重疊的地方,但是都比較有代表性,所以單獨進行舉例說明。
3.1非string類型處理
替換子串非string類型處理,最典型的就是以上舉例中的計算。還有比較典型的就是涉及計數(shù)的問題。
舉例:
源字符串:<a href="http://www.sina.com.cn/">新浪 </a> <a href="http://www.sohu.com/">搜狐 </a> <a href="http://www.qq.com/">騰訊QQ </a> <a href="http://www.163.com/">網(wǎng)易163 </a>
需求:在每個鏈接后面加編號,結(jié)果
<a href="http://www.sina.com.cn/">新浪 </a>01 <a href="http://www.sohu.com/">搜狐 </a>02 <a href="http://www.qq.com/">騰訊QQ </a>03 <a href="http://www.163.com/">網(wǎng)易163 </a>04
代碼實現(xiàn):
string test = "<a href=\"http://www.sina.com.cn/\">新浪</a><a href=\"http://www.sohu.com/\">搜狐</a><a href=\"http://www.qq.com/\">騰訊QQ</a><a href=\"http://www.163.com/\">網(wǎng)易163</a>"; Regex reg = new Regex(@"(?is)<a[^>]*>(?:(?!</?a\b).)*</a>"); int i = 1; string result = reg.Replace(test, delegate(Match m) { return m.Value + (i++).ToString("00"); }); richTextBox2.Text = result; /*--------輸出-------- <a rel="external nofollow" >新浪</a>01<a rel="external nofollow" >搜狐</a>02<a rel="external nofollow" >騰訊QQ</a>03<a rel="external nofollow" >網(wǎng)易163</a>04 */
這個需求是在鏈接后加編號,只要匹配到<a…>…</a>標(biāo)簽,在后面加上編號即可,但是由于編號是要根據(jù)a標(biāo)簽的個數(shù)來計數(shù)的,所以是動態(tài)變化的,這樣直接替換就做不到了。而正則中的委托,是每次匹配成功后都會調(diào)用委托方法,而匹配是從左向右按順序匹配的,所以調(diào)用委托方法也是按匹配的先后順序進行調(diào)用的,這樣就可以先用正則匹配出a標(biāo)簽,然后在委托方法中動態(tài)進行計數(shù)了。
3.2邏輯判斷
如果待替換的子串,需要根據(jù)當(dāng)前匹配子串的內(nèi)容,經(jīng)過判斷后決定如何替換,一般無法直接通過replace()實現(xiàn),需求在委托方法里進行判斷。
舉例1:
源字符串:源字符串規(guī)律為“字母=數(shù)字”,用“&”相連
a=12&b=34&c=56&d=78
a=98&b=76&d=54
需求:如果源字符串有“c=數(shù)字”,就替換為“c=12”,否則在字符串結(jié)尾添加“&c=98”。
代碼實現(xiàn):
string[] test = new string[]{"a=12&b=34&c=56&d=78", "a=98&b=76&d=54"}; Regex reg = new Regex(@"(?is)(?<=^(?:(?!c=).)*)(?(c=[^&]+)c=[^&]+|$)"); foreach(string s in test) { richTextBox2.Text += "字符串: " + s + "\n"; richTextBox2.Text += "替換后: " + reg.Replace(s, delegate(Match m) { return m.Value == "" ? "&c=98" : "c=12"; }) + "\n\n"; } /*--------輸出-------- 字符串: a=12&b=34&c=56&d=78 替換后: a=12&b=34&c=12&d=78 字符串: a=98&b=76&d=54 替換后: a=98&b=76&d=54&c=98 */
還有一個類似的需求實例。
舉例2(一個可能很簡單的正式表達式求助):
源字符串:要處理的字符有可能是
""(空)
"p=1"
"ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a"
"ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a&p=2"
"ID=e2798a59&xx=79d5&p=4&bb=4833-9c57&cc=87d46a8&bb=b907a"
需求:對上述任何一種字符串的可能,查找是否有p=x,如果找不到,為字符串加上"p=0" ,如果找到,還要得到x的值,讓y=x+1之后,再把"p=y"替換之前的p=x。
代碼實現(xiàn):
string[] test = new string[] { "", "p=1", "ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a", "ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a&p=2", "ID=e2798a59&xx=79d5&p=4&bb=4833-9c57&cc=87d46a8&bb=b907a" }; foreach (string s in test) { richTextBox2.Text += "原始字符串: \t" + s + "\n"; richTextBox2.Text += "替換后字符串: \t" + Regex.Replace(s, @"(?is)p=(?<v>\d+)|(?<!p=\d+.*)$", delegate(Match m) { if (m.Groups["v"].Value != "") return "p=" + (Convert.ToInt32(m.Groups["v"].Value) + 1); return "p=0"; }) + "\n\n"; } /*--------輸出-------- 原始字符串: 替換后字符串: p=0 原始字符串: p=1 替換后字符串: p=2 原始字符串: ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a 替換后字符串: ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907ap=0 原始字符串: ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a&p=2 替換后字符串: ID=e2798a59&xx=79d5&bb=4833-9c57&cc=87d46a8&bb=b907a&p=3 原始字符串: ID=e2798a59&xx=79d5&p=4&bb=4833-9c57&cc=87d46a8&bb=b907a 替換后字符串: ID=e2798a59&xx=79d5&p=5&bb=4833-9c57&cc=87d46a8&bb=b907a */
這個需求中,既涉及到了對替換子串的邏輯判斷,又涉及到了數(shù)字運算,直接替換做不到,所以要考慮使用委托。先通過正則匹本出p=x,再在委托方法中進行邏輯判斷和運算。
3.3 多條件組合替換
當(dāng)需求中的條件多于一個時,可能無法在一個正則表達式中進行判斷,或者即使能夠在一個正則表達式中判斷,由于正則表達式非常復(fù)雜,會降低匹配效率,所以還是要在委托方法中進行替換。
舉例1:
源字符串:第一個測試...<a href=\"www.test.com\">又一個測試</a>...第三個測試...<a href=\"www.test.com\" title=\"測試\" >第幾個測試了?</a>...這是最后一個測試了...
需求:為字符串中的“測試”加鏈接,已有鏈接的不加。
這個需求,首先是要進行替換,但又加了一個附加條件,已有鏈接的不替換,這樣如果在一個正則表達式中實現(xiàn),正則太復(fù)雜,不但降低匹配效率,擴展起來也很困難,可讀性也差,所以還是用正則委托來實現(xiàn)比較好。
先分析一下需求,在<a…>…</a>標(biāo)簽內(nèi)的關(guān)鍵字不進行替換,那換個角度,只要先找出a標(biāo)簽外的字符串,對關(guān)鍵字進行替換就可以滿足需求了。所以就是寫正則,匹配出a標(biāo)簽外的子串,在委托方法中對關(guān)鍵字加鏈接,再替換回原字符串就可以了。
代碼實現(xiàn):
string test = "第一個測試...<a href=\"www.test.com\">又一個測試</a>...第三個測試...<a href=\"www.test.com\" title=\"測試\" >第幾個測試了?</a>...這是最后一個測試了..."; Regex reg = new Regex(@"(?is)^((?!</?a).)+|</a>((?!</?a).)+"); string result = reg.Replace(test, delegate(Match m) { return m.Value.Replace("測試", "<a href=\"www.test.com\">測試</a>"); }); richTextBox2.Text = result; /*--------輸出-------- 第一個<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >測試</a>...<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >又一個測試</a>...第三個<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >測試</a>...<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="測試" >第幾個測試了?</a>...這是最后一個<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >測試</a>了... */
當(dāng)然,這個例子并不嚴謹,因為其它標(biāo)簽中也可能出現(xiàn)關(guān)鍵字,而這些關(guān)鍵通常也是不應(yīng)該被替換的,這時也可以在委托方法中進行判斷,以確定是否應(yīng)該被替換。
舉例2(正則去除不包含特定字符串的A標(biāo)簽~):
源字符串:<a href=www.abc.com>abc </a>啊啊啊 <a href=bcd.com>abc </a>啊啊啊 <a href="www.abc.com" class="t1">abc </a>啊啊啊 <a href=def.com>abc </a>啊啊啊 <a href=efg.com>abc </a>
需求:把鏈接中不包含“abc”的超鏈接過濾掉。
這個需求,實際上也是兩個條件,首先是要做替換,然后附加了一個條件,鏈接中不包含“abc”的替換。類似于這種符合某一規(guī)律的子串,部分替換,部分保留的情況,通常比較適合用正則委托來解決。
當(dāng)然,這個需求還是可以直接通過一個正則表達式來處理的,先看一下這種處理方式的代碼。
string test = "<a href=www.abc.com>abc </a>啊啊啊 <a href=bcd.com>abc </a>啊啊啊 <a href=\"www.abc.com\" class=\"t1\">abc </a>啊啊啊 <a href=def.com>abc </a>啊啊啊 <a href=efg.com>abc </a> "; Regex reg = new Regex(@"(?is)<a(?:(?!href=).)*href=(['""]?)(?:(?!abc|['""\s>]).)+\1(?:\s[^>]*)?>((?:(?!</?a\b).)*)</a>"); string result = reg.Replace(test, "$2"); richTextBox2.Text = result; /*--------輸出-------- <a href=www.abc.com>abc </a>啊啊啊 abc 啊啊啊 <a href="www.abc.com" rel="external nofollow" rel="external nofollow" class="t1">abc </a>啊啊啊 abc 啊啊啊 abc */
可以看到,這種處理方式,是先進行判斷,再進行匹配。在正則表達式中,對鏈接子串的每一個字符用“(?!abc|['""\s>]).”進行判斷,所以有多少個字符,就要判斷多少次,在這種情況下,通常需要使用“|”來對不同的條件取“或”,而“|”的效率一般是比較低的。
另一種處理方式,是先把鏈接匹配出來,然后在委托方法中進行判斷,以決定是否替換。
代碼實現(xiàn):
string test = "<a href=www.abc.com>abc </a>啊啊啊 <a href=bcd.com>abc </a>啊啊啊 <a href=\"www.abc.com\" class=\"t1\">abc </a>啊啊啊 <a href=def.com>abc </a>啊啊啊 <a href=efg.com>abc </a> "; Regex reg = new Regex(@"(?is)<a(?:(?!href=).)*href=(['""]?)([^'""\s>]+)\1[^>]*>((?:(?!</?a\b).)*)</a>"); string result = reg.Replace(test, delegate(Match m) { if (m.Groups[2].Value.IndexOf("abc") > -1) return m.Value; return m.Groups[3].Value; }); richTextBox2.Text = result; /*--------輸出-------- <a href=www.abc.com>abc </a>啊啊啊 abc 啊啊啊 <a href="www.abc.com" rel="external nofollow" rel="external nofollow" class="t1">abc </a>啊啊啊 abc 啊啊啊 abc */
這種處理方式,是先進行匹配,再進行判斷。先通過正則把每一個鏈接都匹配出來,作為參數(shù)傳給委托方法,在委托方法中判斷是否包含“abc”,以決定是否替換。這種方式因為匹配過程中不需要進行判斷,所以匹配的速度是很快的,然后在委托方法中只執(zhí)行一次判斷即可。兩種處理方式的效率,在字符較少時區(qū)別不大,在字符較多,調(diào)用較頻繁的情況下,還是委托方法的效率比較高。
類似于這種需求,在效率、可讀性、可擴展性等方面綜合考慮,還是使用委托方法要好一些。
到此這篇關(guān)于.NET正則基礎(chǔ)之正則委托的文章就介紹到這了,更多相關(guān)正則委托內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
判斷用戶輸入的銀行卡號是否正確的方法(基于Luhn算法的格式校驗)
下面小編就為大家?guī)硪黄袛嘤脩糨斎氲你y行卡號是否正確的方法(基于Luhn算法的格式校驗)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04