【问题标题】:Regular Expressions in C# running slowlyC#中的正则表达式运行缓慢
【发布时间】:2011-10-04 00:02:36
【问题描述】:

在过去的一周里,我一直在使用 regex 做一些工作,并取得了很大的进步,但是,我仍然相当 n00b。我有一个用 C# 编写的正则表达式:

string isMethodRegex = 
    @"\b(public|private|internal|protected)?\s*(static|virtual|abstract)?"+
    @"\s*(?<returnType>[a-zA-Z\<\>_1-9]*)\s(?<method>[a-zA-Z\<\>_1-9]+)\s*\"+
    @"((?<parameters>(([a-zA-Z\[\]\<\>_1-9]*\s*[a-zA-Z_1-9]*\s*)[,]?\s*)+)\)";
IsMethodRegex = new Regex(isMethodRegex);

由于某种原因,当调用正则表达式 IsMethodRegex.IsMatch() 时,它会在以下字符串上挂起 30 多秒:

"\t * Returns collection of active STOP transactions (transaction type 30) "

有谁知道 Regex 的内部是如何工作的,以及为什么在匹配这个字符串而不是其他字符串时会这么慢。我玩过它,发现如果我去掉 * 和括号,它就可以正常运行。也许正则表达式写得不好?

任何帮助将不胜感激。

【问题讨论】:

  • 你试过编译正则表达式吗?其中一个构造函数重载提供了这种能力。
  • @Steve:我刚刚测试了自己,编译后仍然需要很长时间。我可以在我自己的机器上用 LinqPad 重现很长时间。
  • 我知道这不是重点,但你的正则表达式对我来说看起来很脆弱。不知何故,我认为性能是你的问题中最少的。
  • 解释一下你试图解决的实际问题怎么样?
  • 这可能会提供一些关于注意事项的见解:regular-expressions.info/catastrophic.html

标签: c# regex performance


【解决方案1】:

编辑:我认为性能问题来自&lt;parameters&gt; 匹配组的完成方式。我已经重新排列以匹配第一个参数,然后是任意数量的连续参数,或者根本不匹配。此外,我已将参数类型和名称之间的 \s* 更改为 \s+ (我认为这是导致很多回溯的原因,因为它不允许空格,因此 object 可以匹配为 object\s* 不匹配空格),它似乎运行得更快:

string isMethodRegex = 
    @"\b(public|private|internal|protected)?\s*(static|virtual|abstract)?"+
    @"\s*(?<returnType>[a-zA-Z\<\>_1-9]*)\s*(?<method>[a-zA-Z\<\>_1-9]+)\s*\"+
    @"((?<parameters>((\s*[a-zA-Z\[\]\<\>_1-9]*\s+[a-zA-Z_1-9]*\s*)"+
    @"(\s*,\s*[a-zA-Z\[\]\<\>_1-9]*\s+[a-zA-Z_1-9]*\s*)*\s*))?\)";

编辑:正如@Dan 适当指出的那样,以下仅仅是因为 Regex 可以提前退出。

这确实是一个非常奇怪的情况,但是如果我在开头删除两个可选匹配(对于公共/私有/内部/受保护和静态/虚拟/抽象),那么它几乎会立即再次开始运行:

string isMethodRegex = 
    @"\b(public|private|internal|protected)\s*(static|virtual|abstract)"+
    @"(?<returnType>[a-zA-Z\<\>_1-9]*)\s(?<method>[a-zA-Z\<\>_1-9]+)\s*\"+
    @"((?<parameters>(([a-zA-Z\[\]\<\>_1-9]*\s*[a-zA-Z_1-9]*\s*)[,]?\s*)+)\)";
var IsMethodRegex = new Regex(isMethodRegex);

string s = "\t * Returns collection of active STOP transactions (transaction type 30) ";

Console.WriteLine(IsMethodRegex.IsMatch(s));

从技术上讲,您可以将每种可能的正则表达式分成四个单独的正则表达式来处理这种特殊情况。但是,当您尝试处理越来越复杂的场景时,您可能会一次又一次地遇到此性能问题,因此这可能不是理想的方法。

【讨论】:

  • 这个结果是'假',对吗?它可以立即排除匹配,因为它不是以“p”或“i”开头的,所以这是一个微不足道的搜索。
  • @Dan:确实你是对的。我怀疑这与&lt;parameters&gt; 部分的完成方式有关。
【解决方案2】:

我用 1 个或多个 (+) 更改了一些 0 个或多个 (*) 匹配,我认为 这对您的正则表达式有意义(它比 Java 和 C# 更适合到 VB.NET):

string isMethodRegex =
  @"\b(public|private|internal|protected)?\s*(static|virtual|abstract)?" +
  @"\s*(?<returnType>[a-zA-Z\<\>_1-9]+)\s+(?<method>[a-zA-Z\<\>_1-9]+)\s+\" +
  @"((?<parameters>(([a-zA-Z\[\]\<\>_1-9]+\s+[a-zA-Z_1-9]+\s*)[,]?\s*)+)\)";

现在很快。

请检查它是否仍然返回您期望的结果。

有关不良正则表达式的一些背景信息,请查看here

【讨论】:

    【解决方案3】:

    您是否尝试过编译您的正则表达式?

    string pattern = @"\b[at]\w+";
    RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Compiled;
    string text = "The threaded application ate up the thread pool as it executed.";
    MatchCollection matches;
    
    Regex optionRegex = new Regex(pattern, options);
    Console.WriteLine("Parsing '{0}' with options {1}:", text, options.ToString());
    // Get matches of pattern in text
    matches = optionRegex.Matches(text);
    // Iterate matches
    for (int ctr = 1; ctr <= matches.Count; ctr++)
       Console.WriteLine("{0}. {1}", ctr, matches[ctr-1].Value);
    

    那么正则表达式只是在第一次执行时很慢。

    【讨论】:

    • 我们已经尝试过了,没有任何区别。其实前段时间有个回答说的和作者删的一样。
    猜你喜欢
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 1970-01-01
    • 2023-03-17
    • 2012-12-21
    • 2011-03-08
    • 1970-01-01
    相关资源
    最近更新 更多