使用静态Regex.IsMatch与创建Regex的实例

Ben*_*iel 43 c# regex optimization

在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();
}
Run Code Online (Sandbox Code Playgroud)

或者你应该坚持包含重要模式的正则表达式的实例?使用Regex.IsMatch的成本是多少?我想在每个正则表达式中都会创建一个NFA.根据我的理解,这个NFA的创作并非无足轻重.

P D*_*ddy 49

与我典型的自我中心有很大的不同,我有点想要回避这个问题.

我在下面保留的原始答案是基于对.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);
}
Run Code Online (Sandbox Code Playgroud)

这里的显着差异是true第三个论点.这对应于名为"useCache"的参数.如果是这样,则在第二次和后续使用时从缓存中检索解析的树.

这种缓存会占用静态和实例方法之间的大多数但不是全部的性能差异.在我的测试中,静态IsMatch方法仍然比实例方法慢约20%,但是当在一组10,000个输入字符串上运行100次时(总共100万次操作),这只增加了大约半秒.

在某些情况下,这20%的减速仍然很重要.如果你发现自己正在重复数亿个字符串,你可能希望尽一切可能使它更有效率.但我敢打赌99%的时间,你使用特定的正则表达不会超过几次,并且你输入静态方法的额外毫秒甚至不会引人注意.

关于devgeezer的道具,几乎在一年前就指出了这一点,尽管似乎没有人注意到.

我的老答案如下:


静态IsMatch函数定义如下:

public static bool IsMatch(string input, string pattern){
    return new Regex(pattern).IsMatch(input);
}
Run Code Online (Sandbox Code Playgroud)

而且,是的,Regex对象的初始化并非易事.您应该使用静态IsMatch(或任何其他静态Regex函数)作为仅使用一次的模式的快捷方式.如果您将重用该模式,那么重用一个Regex对象也是值得的.

至于你是否应该RegexOptions.Compiled像Jon Skeet所说的那样指明这是另一个故事.答案是:它取决于.对于简单模式或仅使用少数几次的模式,使用非编译实例可能会更快.在决定之前你一定要做好准备.编译正则表达式对象的成本确实非常大,可能不值得.


举个例子,如下:

const int count = 10000;

string pattern = "^[a-z]+[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);
Run Code Online (Sandbox Code Playgroud)

count = 10000,作为列出的,所述第二输出是最快的.提高count100000和编译的版本获胜.


Jon*_*eet 32

如果您要多次重用正则表达式,我会创建它RegexOptions.Compiled并缓存它.让框架在每次需要时解析正则表达式都没有意义.

  • 我的意思是"将其保存在静态只读变量中". (4认同)

dev*_*zer 12

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

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

'true'值用于名为"useCache"的构造函数参数.所有的正则表达式构造函数最终都是通过这个构造函数链接的,静态函数直接称之为 - 传递"真实".

您了解更多关于BCL博客文章有关优化正则表达式性能突出的静态方法的缓存使用这里.此博客文章还引用了性能测量.阅读一系列关于优化Regex性能的博客文章是一个很好的起点.


And*_*are 8

我同意Jon的意见,只是为了澄清它看起来像这样:

static Regex regex = new Regex("regex", RegexOptions.Compiled);
Run Code Online (Sandbox Code Playgroud)

它还值得看一下RegexOptions其他可能有用的标志的枚举.


Ben*_*ngs 6

有许多因素会影响使用正则表达式的性能.最终,在您的情况下找出最高性能的唯一方法是使用尽可能真实的情况进行测量.

在该网页汇编和重用 MSDN上的正则表达式的对象涵盖了这一点.总之,它说

  1. 编译的正则表达式需要一些时间来编译,一旦编译,它们的内存只会在AppDomain卸载时释放.是否应该使用编译取决于您使用的模式数量以及使用频率.

  2. 静态Regex方法缓存最后15个(默认情况下)模式的已解析正则表达式表示.因此,如果您没有在应用程序中使用许多不同的模式,或者您的使用是充分聚集的,那么缓存实例或缓存它的框架之间没有太大区别.