使用静态Regex.IsMatch vs创build正则expression式的一个实例

在C#中你应该有这样的代码:

public static string importantRegex = "magic!"; public void F1(){ //code if(Regex.IsMatch(importantRegex)){ //codez in here. } //more code } public void main(){ F1(); /* some stuff happens...... */ F1(); } 

还是应该坚持一个包含重要模式的正则expression式的实例呢? 使用Regex.IsMatch的成本是多less? 我想在每个正则expression式中都有一个NFA。 从我的理解来看,这个NFA的创造并不是微不足道的。

在与我典型的自负主义相去甚远的情况下,我对这个答案有些反感。

我原来的答案保留在下面,是基于对.NET框架1.1版本的考察。 这是非常可耻的,因为在我的回答中,.NET 2.0已经出现了三年多了,它包含了对Regex类的改变,它显着影响了静态方法和实例方法之间的差异。

在.NET 2.0(和4.0)中,静态IsMatch函数的定义如下:

 public static bool IsMatch(string input, string pattern){ return new Regex(pattern, RegexOptions.None, true).IsMatch(input); } 

这里的显着差异在于第三个论点是没有什么意义的。 这对应于一个名为“useCache”的参数。 如果这是真的,那么在第二次和随后的使用中从caching中检索parsing的树。

这种caching消耗了静态方法和实例方法之间的大部分(但不是全部)性能差异。 在我的testing中,静态IsMatch方法仍然比实例方法慢大约20%,但是在一组10000个inputstring(总共100万次操作)上运行100次时,只有大约半秒钟的增加。

在某些情况下,这种20%的放缓仍然很重要。 如果你发现自己重新编码数以亿计的string,你可能会想要采取每一步,以提高效率。 但我敢打赌,99%的时间,你使用一个特定的正则expression式不超过几次,而你失去了静态方法的额外毫秒将不会很明显。

一位专家指出,大约一年前,虽然似乎没有人注意到这一点。

我的回答如下:


静态IsMatch函数定义如下:

 public static bool IsMatch(string input, string pattern){ return new Regex(pattern).IsMatch(input); } 

而且,是的,正则Regex对象的初始化不是微不足道的。 您应该使用静态IsMatch (或任何其他静态正则Regex函数)作为仅用于一次只能使用的模式的快捷方式。 如果你将重用这个模式,重用一个Regex对象也是值得的。

至于你是否应该指定RegexOptions.Compiled ,正如Jon Skeet所build议的,那是另一回事。 答案是:这取决于。 对于简单的模式或只使用less数几次的模式,使用非编译的实例可能会更快。 在决定之前,你应该确定个人资料。 编译正则expression式对象的代价确实很大,可能不值得。


以下面的例子为例:

 const int count = 10000; string pattern = "^[az]+[0-9]+$"; string input = "abc123"; Stopwatch sw = Stopwatch.StartNew(); for(int i = 0; i < count; i++) Regex.IsMatch(input, pattern); Console.WriteLine("static took {0} seconds.", sw.Elapsed.TotalSeconds); sw.Reset(); sw.Start(); Regex rx = new Regex(pattern); for(int i = 0; i < count; i++) rx.IsMatch(input); Console.WriteLine("instance took {0} seconds.", sw.Elapsed.TotalSeconds); sw.Reset(); sw.Start(); rx = new Regex(pattern, RegexOptions.Compiled); for(int i = 0; i < count; i++) rx.IsMatch(input); Console.WriteLine("compiled took {0} seconds.", sw.Elapsed.TotalSeconds); 

如上所示,在count = 10000 ,第二个输出是最快的。 增加到100000 ,编译版本胜出。

如果要重复使用正则expression式多次,我会使用RegexOptions.Compile创build并caching它。 每当你想要的时候,让框架parsing正则expression式就没有意义了。

对于我在我的机器上的.NET版本,这个答案不再正确。 4.0.30319和2.0.50727对于IsMatch都具有以下function:

 public static bool IsMatch(string input, string pattern) { return new Regex(pattern, RegexOptions.None, true).IsMatch(input); } 

“真”值是一个名为“useCache”的构造函数参数。 所有的Regex构造函数最终都通过这个链接,静态直接调用这个 – 传入“true”。

您可以在BCL博客文章中阅读关于优化Regex性能的更多信息,突出显示这里使用的静态方法的caching。 这篇博文也引用了性能测量。 阅读一系列优化Regex性能的博客文章是一个很好的开始。

有很多事情会影响使用正则expression式的性能。 最终,find你的情况中最高性能的唯一方法就是尽可能使用现实的情况来衡量。

MSDN上正则expression式对象的编译和重用页面涵盖了这一点。 总之,它说

  1. 编译正则expression式需要花费时间进行编译,一旦编译完成,只会在AppDomain卸载它们的内存。 是否应该使用编译将取决于您正在使用的模式的数量以及使用频率。

  2. 静态正则Regex方法caching最近15个(默认)模式的parsing正则expression式表示。 所以如果你的应用程序没有使用许多不同的模式,或者你的用法被充分聚合, 那么caching实例或者caching它的框架就不会有太大的区别。

我同意乔恩,只是为了澄清它看起来像这样:

 static Regex regex = new Regex("regex", RegexOptions.Compiled); 

它也值得去看其他标志RegexOptions枚举,有时可能会有所帮助。

我build议你阅读杰夫的编译正则expression式的职位 。

至于这个问题,如果你问这个问题,这意味着你将只使用一次。 所以,reflection器对Regex.IsMatch的反汇编真的没关系:

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

对于我正在处理的WinForm应用程序,我们可以在有效字符上定义一个正则expression式,这个正则expression式可以在每个按键上运行,并且对任何文本框(数据input应用程序)的文本进行validation,所以我使用了caching或编译的正则expression式,例如

  private static Dictionary<string, Regex> regexCache = new Dictionary<string, Regex>(20); 

正则expression式是关键。

然后我有一个静态函数,我可以调用时validation数据:

 public static bool RegExValidate(string text, string regex) { if (!regexCache.ContainsKey(regex)) { Regex compiledRegex = new Regex(regex,RegexOptions.Compiled); regexCache.Add(regex, compiledRegex); } return regexCache[regex].IsMatch(text); }