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倍。附上源代码,感兴趣的读者可自行测试!:-)
这里下载: RegexTest.rar
End.