关键字加链接

这个问题先说一下我的思路,再讨论一下楼主的思路和.NET中一些使用正则需要注意的地方

因为是关键字替换,而关键字的数量也很多,所以从整体上来说有两种实现方式:
一种是在最外层循环关键字,每次替换一个符合条件的关键字,这样每循环一次,就要遍历一次源字符串
另一种是分次找出符合替换条件的子字符串,然后循环关键字进行替换,这样每次处理的源字符串相对较小,但关键字要遍历一次
因为相对于源字符串来说,关键字要小得多,所以一般来说还是后一种效率会高些
按楼主需求的前三条
1、标签内的内容不参与替换,比如: <p title="关键字"> 虽然含有关键字,但不进行替换  
2、本身是链接的内容不参与替换,如: <a>关键字 </a>  
3、 <pre> </pre>标签内的内容不参与替换,如: <pre>关键字 </pre>  
楼主所要替换的关键字,都是在不符合这三条的子字符串中,那就想办法先取出排除了这三种情况的子串,这时候子串中所包含的关键字就是可以替换的了
因为不符合替换需求的条件只有三条,所以一个正则表达式还是可以做到的
取出排除以上三种情况的子串,采用委托方式进行替换
因为楼主的第四个需求
3、尽量做到一个关键字在一篇文章中只有一个被替换成链接(因为关键字很多,都替换成链接,整篇文章十分不雅)
一个关键字只替换一次,那问题就好办了,只要执行过一次替换的关键字,就从关键字列表中去掉,这样不但可以满足楼主的要求,还可以减少每轮循环的次数

再讨论一下楼主的思路和.NET中一些使用正则需要注意的地方吧
可能楼主并不清楚正则中委托的应用,或者是不知道如何通过一个正则,来提取出排除了前三个条件的子串
所以楼主采用了先截取,再拼接的方式,其实跟委托的实现原理基本上是差不多的,只是没有委托这种实现方式效率高而已
再说一下楼主在正则使用中存在的问题吧,涉及到性能问题的时候,需要注意以下几个方面

1、不要使用静态方法,而一定要显式的声明Regex对象
原因是静态方法会临时创建一个Regex对象,用它来调用请求的方法,然后弃用这个对象。而静态方法每次调用,都必须重新检查正则表达式,所以存在效率缺陷  
虽然默认情况下,.NET Framework做了缓存处理,但默认只缓存15个正则表达式,尽管可以通过  
Regex.CacheSize = 123;  
来调整,但这并不是解决效率问题的根本方法

2、不要在循环体中声明Regex对象
因为这样使用,每次都要重新创建对象,重新对正则表达式进行编译,大大降低效率

3、如果正则需要在循环中调用,且循环次数比较多,正则要先在循环体外预编译,RegexOptions.Compiled   
预编译会延长编译时间,占用更多的内存,但会加速匹配过程。一般在数据源较大、正则复杂、频繁调用、循环中使用时,可以考虑进行预编译  
当然,在多处调用,且调用频繁的情况下,可以考虑封装到assembly中  

4、除非对源字符串的结构非常清楚,否则不要轻易使用.*?这种非贪婪模式,非贪婪模式(exp)*?以及量词的嵌套使用不当,会造成无限循环回溯,通常是正则效率陷阱的根源,尽量使用排除型字符组[^…]和否定的正向环视(?!exp)结合贪婪模式来实现
通常情况下,<.+?>是没有<[^>]*>的效率高的

5、匹配失败不需要回溯的子表达式,用固化分组(?>…)加速失败过程,同时避免回溯
那么<[^>]*>通常还可以通过固化分组的方式进行优化<(?>[^>]*)>

所以下面这一部分代码

C# code
   
   
for ( int i = 0 ; i < str1.Count; i ++ ) { tmp = new StringBuilder(str1[i]); if (Regex.Replace(tmp.ToString(), @" <.+?> " , "" ).Length > 5 ) // 可以替换为 Regex tagReg = new Regex( @" <(?>[^>]*)> " , RegexOptions.Compiled); for ( int i = 0 ; i < str1.Count; i ++ ) { tmp = new StringBuilder(str1[i]); if (tagReg.Replace(tmp.ToString(), "" ).Length > 5 )


6、以“|”取“或”的分支结构,对效率的影响也很大,所以通常情况下,使用分支结构时,要尽可能的抽象出相同的规律,对分支的复杂度加以简化
C# code
   
   
( < a . +?</ a > ) | ( < pre( >| /s). +?</ pre > ) // 可以简化为 < (a | pre)/b( ? :( ?!</? / 1 ).) *</ / 1 >

7、在循环中使用,当捕获组没有必须使用的理由时,使用非捕获组代替  
捕获组匹配成功后,会将匹配的内容保存到一个组里,供以后引用,所以无意义的捕获组,会占用内存,降低效率  

所以循环体中
C# code
   
   
reg2 = new Regex( @" ((^|>)[^<]+?)( " + _tag + " ) " , RegexOptions.IgnoreCase); // 以及其后的一行 tmp.Insert(mat2.Index, mat2.Groups[ 1 ].Value + " <a href='/tag/ " + mat2.Groups[ 2 ].Value + " .htm'> " + mat2.Groups[ 2 ].Value + " </a> " ); // 可以替换为 reg2 = new Regex( @" (?<=(?:^|>)(?:(?! " + _tag + " ).)*) " + _tag , RegexOptions.IgnoreCase); tmp.Insert(mat2.Index, " <a href='/tag/ " + mat2.Value + " .htm'> " + mat2.Value + " </a> " );


受限于楼主的实现思路,reg2这个正则,必须是动态生成的,所以没有办法提取到循环体外,此处对性能的影响较大

8、另外,在.NET中动态生成正则表达式时,为了避免存在变量中有正则中具体特殊意义的字符,而导致正则解析失败,抛异常的问题,可以用Regex.Escape()方法对变量进行预处理
C# code
   
   
reg2 = new Regex( @" (?<=(?:^|>)(?:(?! " + Regex.Escape(_tag) + " ).)*) " + Regex.Escape(_tag) , RegexOptions.IgnoreCase);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值