Regex.Replace 方法的性能!
园子里有很多关于去除Html标签的文章。一个常用的经验是使用 Regex.Replace 方法利用正则去替换。这里有一篇使用该方法的文章 C#中如何去除HTML标记 。下面我贴出该方法的代码,见代码清单1-1
代码清单1-1 引用 http://www.cnblogs.com/zoupeiyang/archive/2009/06/22/1508039.html
/// 去除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
string input, // 要修改的字符串
string pattern, //要匹配的正则表达式模式
string replacement, //替换字符串
RegexOptions options //RegexOption 枚举值的按位“或”组合
) // 返回已修改的字符串
用 Reflector 打开System.dll ,在 System.Text.RegularExpressions 命名空间中找到 Regex 类。查看 代码清单1-2中 方法的实现,见代码清单1-3
代码清单1-3
{
return new Regex(pattern, options, true ).Replace(input, replacement);
}
很清楚的看到,该静态方法的内部实现是 实例化了一个 Regex 对象,并调用该对象的一个实例方法。该实例方法见 代码清单1-4
代码清单1-4
{
if (input == null )
{
throw new ArgumentNullException( " input " );
}
return this .Replace(input, replacement, - 1 , this .UseOptionR() ? input.Length : 0 );
}
上面的代码在其内部实现上调用了另一个实例方法。该方法见代码清单1-5
代码清单1-5
{
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.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], - 1 , 0 );
}
}
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
{
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.