Regex.Replace 方法的性能!(090625最新修改)


 Regex.Replace 方法的性能!


    园子里有很多关于去除Html标签的文章。一个常用的经验是使用 Regex.Replace 方法利用正则去替换。这里有一篇使用该方法的文章 C#中如何去除HTML标记 。下面我贴出该方法的代码,见代码清单1-1

代码清单1-1 引用 http://www.cnblogs.com/zoupeiyang/archive/2009/06/22/1508039.html       

        /// <summary>
        
/// 去除HTML标记
        
/// </summary>
        
/// <param name="Htmlstring">包括HTML的源码 </param>
        
/// <returns>已经去除后的文字</returns>
        public static string ReplaceHtmlTag(string Htmlstring)
        {
            
//删除脚本
            Htmlstring = Htmlstring.Replace("\r\n""");
            Htmlstring 
= Regex.Replace(Htmlstring, @"<script.*?</script>""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"<style.*?</style>""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"<.*?>""", RegexOptions.IgnoreCase);
            
//删除HTML
            Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"([\r\n])[\s]+""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"-->""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"<!--.*""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(quot|#34);""\"", RegexOptions.IgnoreCase);
            Htmlstring = Regex.Replace(Htmlstring, @"&(amp|#38);""&", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(lt|#60);""<", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(gt|#62);"">", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(nbsp|#160);""", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(iexcl|#161);""\xa1", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(cent|#162);""\xa2", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(pound|#163);""\xa3", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&(copy|#169);""\xa9", RegexOptions.IgnoreCase);
            Htmlstring 
= Regex.Replace(Htmlstring, @"&#(\d+);""", RegexOptions.IgnoreCase);
            Htmlstring 
= Htmlstring.Replace("<""");
            Htmlstring 
= Htmlstring.Replace(">""");
            Htmlstring 
= Htmlstring.Replace("\r\n""");
            
return Htmlstring;
        }

 

    ReplaceHtmlTag方法内部使用了 Regex 类的静态方法来替换Html标签, Regex.Replace 方法见代码清单1-2

代码清单1-2 

public static string Replace (
    
string input, // 要修改的字符串
    
string pattern, //要匹配的正则表达式模式
    
string replacement, //替换字符串
    RegexOptions options //RegexOption 枚举值的按位“或”组合
) // 返回已修改的字符串

 

    用 Reflector 打开System.dll ,在 System.Text.RegularExpressions 命名空间中找到 Regex 类。查看 代码清单1-2中 方法的实现,见代码清单1-3

代码清单1-3

public static string Replace(string input, string pattern, string replacement, RegexOptions options)
{
    
return new Regex(pattern, options, true).Replace(input, replacement);
}

 

    很清楚的看到,该静态方法的内部实现是 实例化了一个 Regex 对象,并调用该对象的一个实例方法。该实例方法见 代码清单1-4

代码清单1-4 

public string Replace(string input, string replacement)
{
    
if (input == null)
    {
        
throw new ArgumentNullException("input");
    }
    
return this.Replace(input, replacement, -1this.UseOptionR() ? input.Length : 0);

 

    上面的代码在其内部实现上调用了另一个实例方法。该方法见代码清单1-5

代码清单1-5

public string Replace(string input, string replacement, int count, int startat)
{
    
if (input == null)
    {
        
throw new ArgumentNullException("input");
    }
    
if (replacement == null)
    {
        
throw new ArgumentNullException("replacement");
    }
    RegexReplacement replacement2 
= (RegexReplacement) this.replref.Get();
    
if ((replacement2 == null|| !replacement2.Pattern.Equals(replacement))
    {
        replacement2 
= RegexParser.ParseReplacement(replacement, this.caps, this.capsize, this.capnames, this.roptions);
        
this.replref.Cache(replacement2);
    }
    
return replacement2.Replace(this, input, count, startat);
}

 

    重新查看代码清单1-1中的代码,一共调用了 Regex 类的 Replace 方法17次。从代码清单1-3中可以看出,执行代码清单1-1中的ReplaceHtmlTag 方法需要实例化 17个 Regex 对象。如果考虑一个应用在执行一次时需要调用 ReplaceHtmlTag 方法100次,那么就会在内存中实例化 17*100 个对象。如果 Regex.Replace 方法处理的字符串比较小,那么大多数的时间会花费在创建一个新的Regex对象的开销上。这样做显然是不值得的。那有什么方法可以避免不用实例化这么多的对象吗?

    首先我们得把代码1-2中的静态 Replace 方法替换成代码1-5中的实例 Replace 方法。但是调用 1-5中的实例方法时需要创建一个 Regex 对象。那结果不还是需要创建17个对象吗?对,这里确实是需要再创建17个对象,但我们可以利用单件模式把对象的创建工作封装在一个 ReplaceHtml 类中。见代码清单1-6

代码清单1-6

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace RegexTestWin
{
    
public class ReplaceHtml
    {
        
private IList<Regex> _regexs = new List<Regex>();
        
private IList<string> _replacement = new List<string>();

        
private static ReplaceHtml _replaceHtml = null;
        
private static readonly object _object = new object();
        
private ReplaceHtml() { }
        
public static ReplaceHtml Instance
        {
            
get
            {
                
if (_replaceHtml == null)
                {
                    
lock (_object)
                    {
                        
if (_replaceHtml == null)
                        {
                            _replaceHtml 
= SetInstance(new ReplaceHtml());
                        }
                    }
                }
                
return _replaceHtml;
            }
        }        

        
/// <summary>去除Html标签 </summary>
        public string ReplaceHtmlTag(string Htmlstring)
        {
            Htmlstring 
= Htmlstring.Replace("\r\n""");
            Regex aRegex 
= null;
            
for (int count = 0; count < this._replacement.Count; count++)
            {
                aRegex 
= this._regexs[count];
                
if (aRegex != null)
                {
                    Htmlstring 
= aRegex.Replace(Htmlstring, this._replacement[count], -10);
                }
            }
            Htmlstring 
= Htmlstring.Replace("<""");
            Htmlstring 
= Htmlstring.Replace(">""");
            Htmlstring 
= Htmlstring.Replace("\r\n""");
            
return Htmlstring;
        }

        
/// <summary>设置ReplaceHtml的Regex对象 </summary>
        private static ReplaceHtml SetInstance(ReplaceHtml aReplaceHtml)
        {
            
#region 赋值正则表达式和替换后的字符数组
            
string[] pattern = new string[]
            {
                
@"<script.*?</script>",@"<style.*?</style>",@"<.*?>",
                
@"<(.[^>]*)>",@"([\r\n])[\s]+",@"-->",
                
@"<!--.*",@"&(quot|#34);",@"&(amp|#38);",
                
@"&(lt|#60);",@"&(gt|#62);",@"&(nbsp|#160);",
                
@"&(iexcl|#161);",@"&(cent|#162);",@"&(pound|#163);",
                
@"&(copy|#169);",@"&#(\d+);"
            };
            
string[] replacement = new string[]
            {
                
"","","","","","","","\"","&","<",">","","\xa1","\xa2","\xa3","\xa9",""
            };
            
#endregion

            
if (pattern.Length != replacement.Length)
            {
                
throw new Exception("正则表达式数组和替换后的字符数组的长度不一致!");
            }

            
int count = 0//计数器
            foreach (string str in pattern)
            {
                Regex aRegex 
= new Regex(str,RegexOptions.IgnoreCase); //Edit By Old At 2009-06-25
                aReplaceHtml.AddRegex(aRegex, replacement[count]);
                count 
+= 1;
            }
            
return aReplaceHtml;
        }

        
/// <summary>
        
/// 增加一个Regex对象
        
/// </summary>
        
/// <param name="aRegex">Regex 对象</param>
        
/// <param name="Replacement">该对象对应的替换字符串</param>
        private void AddRegex(Regex aRegex, string Replacement)
        {
            _regexs.Add(aRegex);
            _replacement.Add(Replacement);
        }
        
    }
}

    该类的使用如下,见代码清单1-7

代码清单1-7

   public static string ReplaceHtmlTag2(string Htmlstring)
   {
       
return ReplaceHtml.Instance.ReplaceHtmlTag(Htmlstring);
   }

 

    写到这里让我们来测试一下,2种方法在性能的差距。经过测试,在重复执行 ReplaceHtmlTag 方法和ReplaceHtmlTag2 方法 10,100,1000 次后,性能相差在 2-15陪左右。具体见图1-1

 

 图1-1 2种方法执行 1000 次所消耗的时间对比

    说明:该方法在处理短字符串时,性能差距很大。我用新浪的首页做过测试,2种方法的性能只相差1倍。附上源代码,感兴趣的读者可自行测试!:-)

 

090625修正    代码清单1-6 中 private static ReplaceHtml SetInstance(ReplaceHtml aReplaceHtml) 这个方法的实现中,在构造Regex 对象时使用了 public Regex(string pattern) 这个构造函数,忘记了加上 RegexOptions.IgnoreCase 这个枚举。如果想准确的比较2者的性能,需要把 Regex 的构造函数换成带有枚举参数的构造函数。具体修改见代码清单1-6 ,修改后的性能没有上面提到的能达到 15 陪之多,只能提高在 2-5 陪。特此更正!

 

 

    这里下载:  RegexTest.rar

 

   End. 

 

posted @ 2009-06-23 15:11  Old  阅读(4492)  评论(14编辑  收藏  举报