.NET正則基礎(chǔ)之正則委托
1、概述
一般的正則替換,只能對(duì)匹配的子串做簡(jiǎn)單的處理,且只能是做string類(lèi)型的處理之后,作為替換文本替換匹配子串,可以實(shí)現(xiàn)的功能比較有限。.NET的Replace()方法中的replacement參數(shù),不僅可以是字符串,還可以是委托(delegate),在每次匹配成功時(shí),都會(huì)調(diào)用委托方法,對(duì)匹配的子串進(jìn)行處理之后,再作為替換文本返回,匹配子串使用委托方法,可以做任意復(fù)雜的處理,因此這種替換功能非常強(qiáng)大。
委托的類(lèi)型可以是MatchEvaluator,也可以是匿名方法,在每次匹配成功時(shí)調(diào)用。委托方法傳入?yún)?shù)是Match對(duì)象,返回類(lèi)型是string,即正則表達(dá)式在每次匹配成功時(shí),會(huì)得到一個(gè)Match對(duì)象,作為參數(shù)傳給委托方法,做一定處理后,返回替換文本,替換匹配到的子串。
2、委托和匿名方法
在正則替換中使用的委托,一般有兩種方式,顯式聲明的委托和匿名方法。下面以實(shí)例說(shuō)明兩種方式的使用方法。委托和匿名方法的區(qū)別和各自的特點(diǎn)不在這里介紹,請(qǐng)參考相關(guān)文獻(xiàn)或文章。
2.1委托
舉例:
源字符串: a=10, b=20, c=30
需求:將字符串中的數(shù)字加100。
//委托方法 private string regReplace(Match m) { return (Convert.ToInt32(m.Value) + 100).ToString(); } //聲明一個(gè)MatchEvaluator類(lèi)型委托 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匿名方法
事實(shí)上,對(duì)于以上這種簡(jiǎn)單的需求,不需要顯式的聲明委托,直接使用匿名方法即可,
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)用場(chǎng)景
一個(gè)涉及到替換的需求,首先要進(jìn)行分析,是否能夠通過(guò)一個(gè)正則表達(dá)式進(jìn)行直接替換,如果不可以,那就要借助委托了。接下來(lái)就要找出可在委托方法中進(jìn)行處理的子串的規(guī)律,剩下的就是委托方法中最基本的字符串處理了。
正則中委托的典型應(yīng)用場(chǎng)景一般可歸納為以下幾種:
1、替換子串需進(jìn)行非string類(lèi)型的處理,如計(jì)算等;
2、替換子串需經(jīng)過(guò)條件或邏輯判斷來(lái)決定處理方式;
3、多種條件組合的替換。
以上分類(lèi)方式或許有重疊的地方,但是都比較有代表性,所以單獨(dú)進(jìn)行舉例說(shuō)明。
3.1非string類(lèi)型處理
替換子串非string類(lèi)型處理,最典型的就是以上舉例中的計(jì)算。還有比較典型的就是涉及計(jì)數(shù)的問(wèn)題。
舉例:
源字符串:<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>
需求:在每個(gè)鏈接后面加編號(hào),結(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
代碼實(shí)現(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 */
這個(gè)需求是在鏈接后加編號(hào),只要匹配到<a…>…</a>標(biāo)簽,在后面加上編號(hào)即可,但是由于編號(hào)是要根據(jù)a標(biāo)簽的個(gè)數(shù)來(lái)計(jì)數(shù)的,所以是動(dòng)態(tài)變化的,這樣直接替換就做不到了。而正則中的委托,是每次匹配成功后都會(huì)調(diào)用委托方法,而匹配是從左向右按順序匹配的,所以調(diào)用委托方法也是按匹配的先后順序進(jìn)行調(diào)用的,這樣就可以先用正則匹配出a標(biāo)簽,然后在委托方法中動(dòng)態(tài)進(jìn)行計(jì)數(shù)了。
3.2邏輯判斷
如果待替換的子串,需要根據(jù)當(dāng)前匹配子串的內(nèi)容,經(jīng)過(guò)判斷后決定如何替換,一般無(wú)法直接通過(guò)replace()實(shí)現(xiàn),需求在委托方法里進(jì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”。
代碼實(shí)現(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 */
還有一個(gè)類(lèi)似的需求實(shí)例。
舉例2(一個(gè)可能很簡(jiǎn)單的正式表達(dá)式求助):
源字符串:要處理的字符有可能是
""(空)
"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"
需求:對(duì)上述任何一種字符串的可能,查找是否有p=x,如果找不到,為字符串加上"p=0" ,如果找到,還要得到x的值,讓y=x+1之后,再把"p=y"替換之前的p=x。
代碼實(shí)現(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 */
這個(gè)需求中,既涉及到了對(duì)替換子串的邏輯判斷,又涉及到了數(shù)字運(yùn)算,直接替換做不到,所以要考慮使用委托。先通過(guò)正則匹本出p=x,再在委托方法中進(jìn)行邏輯判斷和運(yùn)算。
3.3 多條件組合替換
當(dāng)需求中的條件多于一個(gè)時(shí),可能無(wú)法在一個(gè)正則表達(dá)式中進(jìn)行判斷,或者即使能夠在一個(gè)正則表達(dá)式中判斷,由于正則表達(dá)式非常復(fù)雜,會(huì)降低匹配效率,所以還是要在委托方法中進(jìn)行替換。
舉例1:
源字符串:第一個(gè)測(cè)試...<a href=\"www.test.com\">又一個(gè)測(cè)試</a>...第三個(gè)測(cè)試...<a href=\"www.test.com\" title=\"測(cè)試\" >第幾個(gè)測(cè)試了?</a>...這是最后一個(gè)測(cè)試了...
需求:為字符串中的“測(cè)試”加鏈接,已有鏈接的不加。
這個(gè)需求,首先是要進(jìn)行替換,但又加了一個(gè)附加條件,已有鏈接的不替換,這樣如果在一個(gè)正則表達(dá)式中實(shí)現(xiàn),正則太復(fù)雜,不但降低匹配效率,擴(kuò)展起來(lái)也很困難,可讀性也差,所以還是用正則委托來(lái)實(shí)現(xiàn)比較好。
先分析一下需求,在<a…>…</a>標(biāo)簽內(nèi)的關(guān)鍵字不進(jìn)行替換,那換個(gè)角度,只要先找出a標(biāo)簽外的字符串,對(duì)關(guān)鍵字進(jìn)行替換就可以滿(mǎn)足需求了。所以就是寫(xiě)正則,匹配出a標(biāo)簽外的子串,在委托方法中對(duì)關(guān)鍵字加鏈接,再替換回原字符串就可以了。
代碼實(shí)現(xiàn):
string test = "第一個(gè)測(cè)試...<a href=\"www.test.com\">又一個(gè)測(cè)試</a>...第三個(gè)測(cè)試...<a href=\"www.test.com\" title=\"測(cè)試\" >第幾個(gè)測(cè)試了?</a>...這是最后一個(gè)測(cè)試了..."; Regex reg = new Regex(@"(?is)^((?!</?a).)+|</a>((?!</?a).)+"); string result = reg.Replace(test, delegate(Match m) { return m.Value.Replace("測(cè)試", "<a href=\"www.test.com\">測(cè)試</a>"); }); richTextBox2.Text = result; /*--------輸出-------- 第一個(gè)<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >測(cè)試</a>...<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >又一個(gè)測(cè)試</a>...第三個(gè)<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >測(cè)試</a>...<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" title="測(cè)試" >第幾個(gè)測(cè)試了?</a>...這是最后一個(gè)<a href="www.test.com" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >測(cè)試</a>了... */
當(dāng)然,這個(gè)例子并不嚴(yán)謹(jǐn),因?yàn)槠渌鼧?biāo)簽中也可能出現(xiàn)關(guān)鍵字,而這些關(guān)鍵通常也是不應(yīng)該被替換的,這時(shí)也可以在委托方法中進(jìn)行判斷,以確定是否應(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”的超鏈接過(guò)濾掉。
這個(gè)需求,實(shí)際上也是兩個(gè)條件,首先是要做替換,然后附加了一個(gè)條件,鏈接中不包含“abc”的替換。類(lèi)似于這種符合某一規(guī)律的子串,部分替換,部分保留的情況,通常比較適合用正則委托來(lái)解決。
當(dāng)然,這個(gè)需求還是可以直接通過(guò)一個(gè)正則表達(dá)式來(lái)處理的,先看一下這種處理方式的代碼。
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 */
可以看到,這種處理方式,是先進(jìn)行判斷,再進(jìn)行匹配。在正則表達(dá)式中,對(duì)鏈接子串的每一個(gè)字符用“(?!abc|['""\s>]).”進(jìn)行判斷,所以有多少個(gè)字符,就要判斷多少次,在這種情況下,通常需要使用“|”來(lái)對(duì)不同的條件取“或”,而“|”的效率一般是比較低的。
另一種處理方式,是先把鏈接匹配出來(lái),然后在委托方法中進(jìn)行判斷,以決定是否替換。
代碼實(shí)現(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 */
這種處理方式,是先進(jìn)行匹配,再進(jìn)行判斷。先通過(guò)正則把每一個(gè)鏈接都匹配出來(lái),作為參數(shù)傳給委托方法,在委托方法中判斷是否包含“abc”,以決定是否替換。這種方式因?yàn)槠ヅ溥^(guò)程中不需要進(jìn)行判斷,所以匹配的速度是很快的,然后在委托方法中只執(zhí)行一次判斷即可。兩種處理方式的效率,在字符較少時(shí)區(qū)別不大,在字符較多,調(diào)用較頻繁的情況下,還是委托方法的效率比較高。
類(lèi)似于這種需求,在效率、可讀性、可擴(kuò)展性等方面綜合考慮,還是使用委托方法要好一些。
到此這篇關(guān)于.NET正則基礎(chǔ)之正則委托的文章就介紹到這了,更多相關(guān)正則委托內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript 正則表達(dá)式備忘單實(shí)例代碼
正則表達(dá)式或“regex”用于匹配字符串的各個(gè)部分,下面是我創(chuàng)建正則表達(dá)式的備忘單,需要的朋友可以參考下2019-04-04判斷用戶(hù)輸入的銀行卡號(hào)是否正確的方法(基于Luhn算法的格式校驗(yàn))
下面小編就為大家?guī)?lái)一篇判斷用戶(hù)輸入的銀行卡號(hào)是否正確的方法(基于Luhn算法的格式校驗(yàn))。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04EditPlus中的正則表達(dá)式實(shí)戰(zhàn)(6)
這篇文章主要介紹了EditPlus中的正則表達(dá)式及語(yǔ)義分析,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2016-12-12