源地址

1        概述

一般的正则替换,只能对匹配的子串做简单的处理,且只能是做string类型的处理之后,作为替换文本替换匹配子串,可以实现的功能比较有限。.NET的Replace()方法中的replacement参数,不仅可以是字符串,还可以是委托(delegate),在每次匹配成功时,都会调用委托方法,对匹配的子串进行处理之后,再作为替换文本返回,匹配子串使用委托方法,可以做任意复杂的处理,因此这种替换功能非常强大。

委托的类型可以是MatchEvaluator,也可以是匿名方法,在每次匹配成功时调用。委托方法传入参数是Match对象,返回类型是string,即正则表达式在每次匹配成功时,会得到一个Match对象,作为参数传给委托方法,做一定处理后,返回替换文本,替换匹配到的子串。

2       委托和匿名方法

在正则替换中使用的委托,一般有两种方式,显式声明的委托和匿名方法。下面以实例说明两种方式的使用方法。委托和匿名方法的区别和各自的特点不在这里介绍,请参考相关文献或文章。

2.1     委托

举例

源字符串:a=10, b=20, c=30

需求:将字符串中的数字加100。

//委托方法

private string regReplace(Match m)

{

return (Convert.ToInt32(m.Value) + 100).ToString();

}

//声明一个MatchEvaluator类型委托

MatchEvaluator me = new MatchEvaluator(regReplace);

//正则替换应用

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       正则中委托的典型应用场景

一个涉及到替换的需求,首先要进行分析,是否能够通过一个正则表达式进行直接替换,如果不可以,那就要借助委托了。接下来就要找出可在委托方法中进行处理的子串的规律,剩下的就是委托方法中最基本的字符串处理了。

正则中委托的典型应用场景一般可归纳为以下几种:

1、替换子串需进行非string类型的处理,如计算等;

2、替换子串需经过条件或逻辑判断来决定处理方式;

3、多种条件组合的替换。

以上分类方式或许有重叠的地方,但是都比较有代表性,所以单独进行举例说明。

3.1     非string类型处理

替换子串非string类型处理,最典型的就是以上举例中的计算。还有比较典型的就是涉及计数的问题。

举例

源字符串:<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/">网易163 </a>

需求:在每个链接后面加编号,结果

<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/">网易163 </a>04

代码实现:

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/\">网易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 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/">网易163</a>04

*/

这个需求是在链接后加编号,只要匹配到<a…>…</a>标签,在后面加上编号即可,但是由于编号是要根据a标签的个数来计数的,所以是动态变化的,这样直接替换就做不到了。而正则中的委托,是每次匹配成功后都会调用委托方法,而匹配是从左向右按顺序匹配的,所以调用委托方法也是按匹配的先后顺序进行调用的,这样就可以先用正则匹配出a标签,然后在委托方法中动态进行计数了。

3.2     逻辑判断

如果待替换的子串,需要根据当前匹配子串的内容,经过判断后决定如何替换,一般无法直接通过replace()实现,需求在委托方法里进行判断。

举例1

源字符串:源字符串规律为“字母=数字”,用“&”相连

a=12&b=34&c=56&d=78

a=98&b=76&d=54

需求:如果源字符串有“c=数字”,就替换为“c=12”,否则在字符串结尾添加“&c=98”。

代码实现:

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。

代码实现:

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

*/

这个需求中,既涉及到了对替换子串的逻辑判断,又涉及到了数字运算,直接替换做不到,所以要考虑使用委托。先通过正则匹本出p=x,再在委托方法中进行逻辑判断和运算。

3.3     多条件组合替换

当需求中的条件多于一个时,可能无法在一个正则表达式中进行判断,或者即使能够在一个正则表达式中判断,由于正则表达式非常复杂,会降低匹配效率,所以还是要在委托方法中进行替换。

举例1

源字符串:第一个测试...<a href=\"www.test.com\">又一个测试</a>...第三个测试...<a href=\"www.test.com\" title=\"测试\" >第几个测试了?</a>...这是最后一个测试了...

需求:为字符串中的“测试”加链接,已有链接的不加。

这个需求,首先是要进行替换,但又加了一个附加条件,已有链接的不替换,这样如果在一个正则表达式中实现,正则太复杂,不但降低匹配效率,扩展起来也很困难,可读性也差,所以还是用正则委托来实现比较好。

先分析一下需求,在<a…>…</a>标签内的关键字不进行替换,那换个角度,只要先找出a标签外的字符串,对关键字进行替换就可以满足需求了。所以就是写正则,匹配出a标签外的子串,在委托方法中对关键字加链接,再替换回原字符串就可以了。

代码实现:

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">测试</a>...<a href="www.test.com">又一个测试</a>...第三个<a href="www.test.com">测试</a>...<a href="www.test.com" title="测试" >第几个测试了?</a>...这是最后一个<a href="www.test.com">测试</a>了...

*/

当然,这个例子并不严谨,因为其它标签中也可能出现关键字,而这些关键通常也是不应该被替换的,这时也可以在委托方法中进行判断,以确定是否应该被替换。

举例2正则去除不包含特定字符串的A标签~

源字符串:<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”的替换。类似于这种符合某一规律的子串,部分替换,部分保留的情况,通常比较适合用正则委托来解决。

当然,这个需求还是可以直接通过一个正则表达式来处理的,先看一下这种处理方式的代码。

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" class="t1">abc </a>啊啊啊 abc 啊啊啊 abc

*/

可以看到,这种处理方式,是先进行判断,再进行匹配。在正则表达式中,对链接子串的每一个字符用“(?!abc|['""\s>]).”进行判断,所以有多少个字符,就要判断多少次,在这种情况下,通常需要使用“|”来对不同的条件取“或”,而“|”的效率一般是比较低的。

另一种处理方式,是先把链接匹配出来,然后在委托方法中进行判断,以决定是否替换。

代码实现:

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" class="t1">abc </a>啊啊啊 abc 啊啊啊 abc

*/

这种处理方式,是先进行匹配,再进行判断。先通过正则把每一个链接都匹配出来,作为参数传给委托方法,在委托方法中判断是否包含“abc”,以决定是否替换。这种方式因为匹配过程中不需要进行判断,所以匹配的速度是很快的,然后在委托方法中只执行一次判断即可。两种处理方式的效率,在字符较少时区别不大,在字符较多,调用较频繁的情况下,还是委托方法的效率比较高。

类似于这种需求,在效率、可读性、可扩展性等方面综合考虑,还是使用委托方法要好一些。

转载于:https://www.cnblogs.com/pmars/archive/2011/12/31/2308837.html

【转】.NET正则基础之——正则委托相关推荐

  1. 【转】正则基础之——捕获组(capture group)

    源地址 1        概述 1.1     什么是捕获组 捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用.当然,这种引用既可以是在正则表达式内部, ...

  2. 正则基础之 NFA引擎匹配原理

    来源:http://www.jb51.net/article/19332.htm 1 为什么要了解引擎匹配原理 一个个音符杂乱无章的组合在一起,弹奏出的或许就是噪音,同样的音符经过作曲家的手,就可以谱 ...

  3. Java提取文本文档中的所有网址(小案例介绍正则基础知识)

    正则表达式基础以及Java中使用正则查找 定义: 正则表达式是一些用来匹配和处理文本的字符串 正则的基础(先大致了解下) 1. 正则表达式的作用 查找特定的信息(搜索) 替换一些文本(替换) 2. 正 ...

  4. 正则基础之——反向引用

    From: http://blog.csdn.net/lxcnn/article/details/4476746 1        概述 捕获组捕获到的内容,不仅可以在正则表达式外部通过程序进行引用, ...

  5. 正则基础之——非捕获组

    非捕获组:(?:Expression) 接触正则表达式不久的人,通常都会对非捕获比较迷惑,为什么要有非捕获组?作用是什么?应该在什么场景下使用? 说到非捕获组,首先要了解什么是捕获组,详细内容参考 正 ...

  6. .NET正则基础之——平衡组

    1        概述 平衡组是微软在.NET中提出的一个概念,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配..NET是目前对正则支持最完备.功能最强大的语言平台之一,而平衡组正是其强大 ...

  7. 正则基础之——捕获组(capture group)

    1        概述 1.1     什么是捕获组 捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或显式命名的组里,方便后面引用.当然,这种引用既可以是在正则表达式内部,也可以是 ...

  8. 正则基础知识学起来很简单

    正则基础知识学起来很简单 第一.正则表达式,也称为模式表达式,它有自己一套完整编写规范. 正则表达式的用途:正则表达式主要是用来对字符串进行匹配.查找.替换和分割等操作. 第二.正则表达式主要是由四部 ...

  9. 正则基础之——贪婪与非贪婪模式

    正则基础之--贪婪与非贪婪模式 转自:正则基础之--贪婪与非贪婪模式 1 概述 贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为,贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模 ...

最新文章

  1. autojs微博_js实现微博发布小功能
  2. 王立众:学习多媒体开发从编解码开始
  3. ADO方法访问数据库的封装接口
  4. 20145234黄斐《网络对抗技术》实验六-信息搜集与漏洞扫描
  5. rest framework 权限
  6. Windows如何ping端口
  7. 【数据库】数据库课程设计一一疫苗接种数据库
  8. python 声音基频f0_如何得到一个曲子的基频?
  9. (转)iOS 上的相机捕捉
  10. SAP ABAP 热咖啡报表模板
  11. CGB2104-Day17
  12. Nim游戏和SG函数
  13. sql语句操作(2)
  14. centos yum 安装jdk
  15. Python PIL.Image之制作GIF图片
  16. HelloPool正式当选MDUKEY验证节点,助力MDU生态繁荣
  17. Helium白皮书 2018-11版
  18. 《写给大家看的设计书》(第四版)
  19. 海量数据Top-k问题如何处理以及大数据查询如何优化
  20. 【编码规范篇】| C#编码规范 代码规范总结,包括命名规范,代码规范 注释规范等

热门文章

  1. 根据输入的日期计算你活了多少天(新手)
  2. Linux从入门到精通——自动安装脚本
  3. 在Windows Server 2012 R2中搭建SQL Server 2012故障转移集群
  4. 02_记录学生相关数据,输出平均分数
  5. About darwin OS
  6. 原型链Object的一些方法
  7. noi 7221 拯救公主 (状态压缩+bfs)
  8. 解决使用mybatis分页插件PageHelper的一个报错问题
  9. Python中获取异常(try Exception)信息
  10. Android 解决RecyclerView删除Item导致位置错乱的问题