思想:
对一篇文章查找搜索关键字,并进行替换,变成链接形式要求:
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.   

    把List<string> str1 = new List<string>();
    List<string> str2 = new List<string>();
    修改为
    Dictionary<TKey, TValue>看看执行效果
      

  2.   


    建议不错,但 Dictionary 较适合查找,用在循环上效率不佳
      

  3.   

    原因一得知,
    reg2 = new Regex(@"((^|>)[^<]+?)(" + _tag + ")", RegexOptions.IgnoreCase);
    这句正则执行速度有点慢,加上外边那个foreach循环要进行上千次,导致用时达到30秒以上(如果body长度3000左右)CPU使用100%
    估计效率提高空间不大,除非改变思路
      

  4.   

    按我的思路大致处理完成,因为没有实际数据,所以需求符合性和性能没法测试,楼主测试吧,如果有不符合的地方,给出实例List<string> tags = new List<string>(new string[]{ "关键字1", "关键字2", "关键字3", "关键字4", "关键字5" });
    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;请楼主先验正我给出的代码的有效性和效率,然后再说思路和实现,以及楼主所写代码缺陷所在
      

  5.   

    其它部分不变,正则表达式稍稍做了下优化Regex reg = new Regex(@"(?:^|(?<!<(?:a|pre)\b(?>[^<>]*))>)(?>[^<>]*)(?:<|$)", RegexOptions.IgnoreCase| RegexOptions.Compiled);
      

  6.   

    这个问题先说一下我的思路,再讨论一下楼主的思路和.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、匹配失败不需要回溯的子表达式,用固化分组(?>…)加速失败过程,同时避免回溯
    那么<[^>]*>通常还可以通过固化分组的方式进行优化<(?>[^>]*)>所以下面这一部分代码
    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);
      

  7.   

    for (int i = 0; i < str1.Count; i++)
                {
                    ....                if (....)
                    {
                        ....
                        foreach (string _tag in Tag.AllTags)//AllTags包含了站内所有关键字不懂.net,经验告诉我这些地方让你变慢.谢谢.
      

  8.   

    没有测试数据果然还是有问题的,对于符合条件的子串,有多个关键字时处理错误,而且在foreach中调用tags.Remove(tag);也错了,呵呵,那会就想着睡觉了不过现在是想着上班了,呼呼,实现思路看下就清楚了,实现方式还是有优化空间的List<string> tags = new List<string>(new string[]{ "关键字1", "关键字2", "关键字3", "关键字4", "关键字5" });
    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;
      

  9.   

    谢谢过客的大力支持,马上测试
    不过对于正则 @"(?:^|(?<!<(?:a|pre)\b(?>[^<>]*))>)(?>[^<>]*)(?:<|$)" 还需要慢慢理解~
      

  10.   

    有点小问题,关键字有Google和百度,文章内容是
    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>
      

  11.   

    哈哈,和我要做的一样,参照这个
    http://topic.csdn.net/u/20090601/14/10eb5d82-b3d7-40a3-a53e-f1f2216dc332.html
    我现在是用二进制编码+散列来做,9800多个关键字,关键字长度为4个到17个汉字不等,在10000多字的文本里替换,约0.03秒左右
      

  12.   

    回帖是一种美德!每天回帖即可获得 10 分可用分!http://topic.csdn.net/u/20090601/14/10eb5d82-b3d7-40a3-a53e-f1f2216dc332.html  我先记下来,回头看!
      

  13.   

    sssssssssssssssssssssssssssssssssssssssss
      

  14.   

    楼主啊,你搞得太复杂了。对文章的每个单词(n个)和每个关键字(m个)组合进行正则表达式的查找。当然很慢了。可以这样做,先把不要替换的关键字换掉,比如<p title="关键字">换成<p title="关####键字">
    <a>关键字 </a>换成<a>关####键字 </a>
    <pre>关键字 </pre>换成<pre>关####键字 </pre>然后查找“关键字”替换成链接。
    最后把所有的“####”删掉
      

  15.   

    邓玉娇案子在线播放地址http://tv.newssc.org/VideoPlay.aspx?ID=45824&keepThis=true&
    请一定要点击这个网站,我们要坚决彻底的帮助邓玉娇,我们要将伪政府的行为彻底的暴露在阳光下,我们要维护司法公正!
    CCTV: 女服务员刺死官员,算正当防卫吗?  http://news.cctv.com/special/badong/shouye/index.shtml
    (投票截止日期6月18日,大家请伸出良知的手去救玉娇吧!!!救玉娇救是就明天的自己!!!)