思想:
对一篇文章查找搜索关键字,并进行替换,变成链接形式要求:
1、标签内的内容不参与替换,比如:<p title="关键字"> 虽然含有关键字,但不进行替换
2、本身是链接的内容不参与替换,如:<a>关键字</a>
3、<pre></pre>标签内的内容不参与替换,如:<pre>关键字</pre>
3、尽量做到一个关键字在一篇文章中只有一个被替换成链接(因为关键字很多,都替换成链接,整篇文章十分不雅)本人代码实现如下: public static string FormatBody(string body)
{
Regex reg = new Regex(@"(<a .+?</a>)|(<pre(>|\s).+?</pre>)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match mat = reg.Match(body);
List<string> str1 = new List<string>();
List<string> str2 = new List<string>();
int indx = 0;
while (mat.Success)
{
str1.Add(body.Substring(indx, mat.Index - indx));
str2.Add(mat.Value);
indx = mat.Index + mat.Length; mat = reg.Match(body, indx);
}
str1.Add(body.Substring(indx));
str2.Add(""); StringBuilder sb = new StringBuilder();
int p = 50, cnt, len;
StringBuilder tmp;
Regex reg2;
Match mat2; for (int i = 0; i < str1.Count; i++)
{
tmp = new StringBuilder(str1[i]); if (Regex.Replace(tmp.ToString(), @"<.+?>", "").Length > 5)
{
cnt = tmp.Length / p;
len = 0;
foreach (string _tag in Tag.AllTags)//AllTags包含了站内所有关键字
{
//str = Regex.Replace(str, @"([^<]+?)(" + _tag + ")", "$1<a href='/tag/$2.htm'>$2</a>", RegexOptions.IgnoreCase);
reg2 = new Regex(@"((^|>)[^<]+?)(" + _tag + ")", RegexOptions.IgnoreCase);
mat2 = reg2.Match(tmp.ToString());
if (mat2.Success)
{
tmp.Remove(mat2.Index, mat2.Length);
tmp.Insert(mat2.Index, mat2.Groups[1].Value + "<a href='/tag/" + mat2.Groups[2].Value + ".htm'>" + mat2.Groups[2].Value + "</a>");
if (++len > cnt) break;
}
}
}
sb.Append(tmp.ToString());
sb.Append(str2[i]);
}
return sb.ToString();
}结果是比较满意,可有一个严重的问题——速度太慢
请高手进行优化,或更改一下思路
对一篇文章查找搜索关键字,并进行替换,变成链接形式要求:
1、标签内的内容不参与替换,比如:<p title="关键字"> 虽然含有关键字,但不进行替换
2、本身是链接的内容不参与替换,如:<a>关键字</a>
3、<pre></pre>标签内的内容不参与替换,如:<pre>关键字</pre>
3、尽量做到一个关键字在一篇文章中只有一个被替换成链接(因为关键字很多,都替换成链接,整篇文章十分不雅)本人代码实现如下: public static string FormatBody(string body)
{
Regex reg = new Regex(@"(<a .+?</a>)|(<pre(>|\s).+?</pre>)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match mat = reg.Match(body);
List<string> str1 = new List<string>();
List<string> str2 = new List<string>();
int indx = 0;
while (mat.Success)
{
str1.Add(body.Substring(indx, mat.Index - indx));
str2.Add(mat.Value);
indx = mat.Index + mat.Length; mat = reg.Match(body, indx);
}
str1.Add(body.Substring(indx));
str2.Add(""); StringBuilder sb = new StringBuilder();
int p = 50, cnt, len;
StringBuilder tmp;
Regex reg2;
Match mat2; for (int i = 0; i < str1.Count; i++)
{
tmp = new StringBuilder(str1[i]); if (Regex.Replace(tmp.ToString(), @"<.+?>", "").Length > 5)
{
cnt = tmp.Length / p;
len = 0;
foreach (string _tag in Tag.AllTags)//AllTags包含了站内所有关键字
{
//str = Regex.Replace(str, @"([^<]+?)(" + _tag + ")", "$1<a href='/tag/$2.htm'>$2</a>", RegexOptions.IgnoreCase);
reg2 = new Regex(@"((^|>)[^<]+?)(" + _tag + ")", RegexOptions.IgnoreCase);
mat2 = reg2.Match(tmp.ToString());
if (mat2.Success)
{
tmp.Remove(mat2.Index, mat2.Length);
tmp.Insert(mat2.Index, mat2.Groups[1].Value + "<a href='/tag/" + mat2.Groups[2].Value + ".htm'>" + mat2.Groups[2].Value + "</a>");
if (++len > cnt) break;
}
}
}
sb.Append(tmp.ToString());
sb.Append(str2[i]);
}
return sb.ToString();
}结果是比较满意,可有一个严重的问题——速度太慢
请高手进行优化,或更改一下思路
解决方案 »
- 在本地运行正常的网站发布到空间上就出了问题,有可能是程序写的不够专业的问题吗?
- 急急急
- 配置文件问题:在应用程序级别之外使用注册为 allowDefinition='MachineToApplication' 的节是错误的 asp.net
- asp中方法的继承如何写,省略掉重复的参数定义
- 怎样让文本框的滚动条始终处于最下方??
- 在VS2005中 使用自带的 SQL2005 EXPRESS 创建库。写程序。
- 急~! 高分求解:delphi 和 水晶报表的问题(200)
- ActiveRecord问题
- 一个小问题,马上结贴!!!
- discuz!nt附件上传问题,我快完蛋啦,大哥帮帮我啊
- 使用foreach出现的错误
- 呈现控件时出错
List<string> str2 = new List<string>();
修改为
Dictionary<TKey, TValue>看看执行效果
建议不错,但 Dictionary 较适合查找,用在循环上效率不佳
reg2 = new Regex(@"((^|>)[^<]+?)(" + _tag + ")", RegexOptions.IgnoreCase);
这句正则执行速度有点慢,加上外边那个foreach循环要进行上千次,导致用时达到30秒以上(如果body长度3000左右)CPU使用100%
估计效率提高空间不大,除非改变思路
int index = -1;
private string RegReplace(Match m)
{
foreach (string tag in tags)
{
index = m.Value.IndexOf(tag);
if (index > -1)
{
tags.Remove(tag);
return m.Value.Substring(0,index) + "<a href='/tag/" + tag + ".htm'>" + tag + "</a>" + m.Value.Substring(index + tag.Length);
}
}
return m.Value;
}
//调用
Regex reg = new Regex(@"(?:^|(?<!<(?:a|pre)\b[^<>]*)>)[^<>]*(?:<|$)", RegexOptions.IgnoreCase| RegexOptions.Compiled);
string result = reg.Replace(yourStr, RegReplace);
richTextBox2.Text = result;请楼主先验正我给出的代码的有效性和效率,然后再说思路和实现,以及楼主所写代码缺陷所在
一种是在最外层循环关键字,每次替换一个符合条件的关键字,这样每循环一次,就要遍历一次源字符串
另一种是分次找出符合替换条件的子字符串,然后循环关键字进行替换,这样每次处理的源字符串相对较小,但关键字要遍历一次
因为相对于源字符串来说,关键字要小得多,所以一般来说还是后一种效率会高些
按楼主需求的前三条
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、匹配失败不需要回溯的子表达式,用固化分组(?>…)加速失败过程,同时避免回溯
那么<[^>]*>通常还可以通过固化分组的方式进行优化<(?>[^>]*)>所以下面这一部分代码
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、以“|”取“或”的分支结构,对效率的影响也很大,所以通常情况下,使用分支结构时,要尽可能的抽象出相同的规律,对分支的复杂度加以简化
(<a .+?</a>)|(<pre(>|\s).+?</pre>)
//可以简化为
<(a|pre)\b(?:(?!</?\1).)*</\1>
7、在循环中使用,当捕获组没有必须使用的理由时,使用非捕获组代替
捕获组匹配成功后,会将匹配的内容保存到一个组里,供以后引用,所以无意义的捕获组,会占用内存,降低效率 所以循环体中
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()方法对变量进行预处理
reg2 = new Regex(@"(?<=(?:^|>)(?:(?!" + Regex.Escape(_tag) + ").)*)" + Regex.Escape(_tag) , RegexOptions.IgnoreCase);
{
.... if (....)
{
....
foreach (string _tag in Tag.AllTags)//AllTags包含了站内所有关键字不懂.net,经验告诉我这些地方让你变慢.谢谢.
int index = -1;
string temp = string.Empty;
List<string> list = new List<string>();
private string RegReplace(Match m)
{
temp = m.Value;
foreach (string tag in tags)
{
index = temp.IndexOf(tag);
if (index > -1)
{
list.Add(tag);
temp = temp.Substring(0, index) + "<a href='/tag/" + tag + ".htm'>" + tag + "</a>" + temp.Substring(index + tag.Length);
}
}
foreach (string s in list)
{
tags.Remove(s);
}
list.Clear();
return temp;
}
//调用
Regex reg = new Regex(@"(?:^|(?<!<(?:a|pre)\b(?>[^<>]*))>)(?>[^<>]*)(?:<|$)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
string result = reg.Replace(yourStr, RegReplace);
richTextBox2.Text = result;
不过对于正则 @"(?:^|(?<!<(?:a|pre)\b(?>[^<>]*))>)(?>[^<>]*)(?:<|$)" 还需要慢慢理解~
string yourStr = "<a href=\"www.google.cn\">Google</a>百度";替换后成了
<a href="www.google.cn"><a href='/tag/Google.htm'>Google</a></a><a href='/tag/百度.htm'>百度</a>
http://topic.csdn.net/u/20090601/14/10eb5d82-b3d7-40a3-a53e-f1f2216dc332.html
我现在是用二进制编码+散列来做,9800多个关键字,关键字长度为4个到17个汉字不等,在10000多字的文本里替换,约0.03秒左右
<a>关键字 </a>换成<a>关####键字 </a>
<pre>关键字 </pre>换成<pre>关####键字 </pre>然后查找“关键字”替换成链接。
最后把所有的“####”删掉
请一定要点击这个网站,我们要坚决彻底的帮助邓玉娇,我们要将伪政府的行为彻底的暴露在阳光下,我们要维护司法公正!
CCTV: 女服务员刺死官员,算正当防卫吗? http://news.cctv.com/special/badong/shouye/index.shtml
(投票截止日期6月18日,大家请伸出良知的手去救玉娇吧!!!救玉娇救是就明天的自己!!!)