运行一堆RegExes时,C#程序会冻结一分钟

时间:2012-01-07 23:25:46

标签: c# regex performance

我有一个程序在相当长的文本集上运行大量正则表达式(10+)(5-15个文本,每个约1000个单词)

每次完成后我觉得我忘记了某处的Thread.Sleep(5000)。正则表达式真的是处理器重的还是什么?看起来计算机应该在一毫秒内完成这样的任务。

我应该尝试将所有正则表达式分组为一个怪物表达式吗?那会有帮助吗?

由于

编辑:这是一个正在运行的正则表达式:

Regex rgx = new Regex(@"((.*(\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*).*)|(.*(keyword1)).*|.*(keyword2).*|.*(keyword3).*|.*(keyword4).*|.*(keyword5).*|.*(keyword6).*|.*(keyword7).*|.*(keyword8).*|.*(count:\n[0-9]|count:\n\n[0-9]|Count:\n[0-9]|Count:\n\n[0-9]|Count:\n).*|.*(keyword10).*|.*(summary: \n|Summary:\n).*|.*(count:).*)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

Regex regex = new Regex(@".*(\.com|\.biz|\.net|\.org|\.co\.uk|\.bz|\.info|\.us|\.cm|(<a href=)).*", RegexOptions.Compiled | RegexOptions.IgnoreCase);

这是非常巨大的,毫无疑问。这个想法是,如果它涉及任何关键字或链接,它将只取出围绕它的整个段落。

4 个答案:

答案 0 :(得分:2)

正则表达式不会杀死CPU,正则表达式的作者会这样做。 ;)

但严重的是,如果正则表达式总是像你描述的那样慢,那么就没有人会使用它们。在你开始加载像Compiled选项之类的银色项目符号之前,你应该回到你的正则表达式,看看它是否可以改进。

它可以。每个关键字都在它自己的分支/替代中,每个分支都以.*开头,因此每个分支所做的第一件事就是消耗当前段落的剩余部分(即,直到下一个换行的所有内容)。然后它在尝试匹配关键字时开始回溯。如果它回到它开始的位置,下一个分支接管并做同样的事情。

当所有分支都报告失败时,正则表达式引擎会向前突破一个位置并再次遍历所有分支。这是十几个分支,乘以段落中的字符数,乘以段落的数量......我想你明白了。与此正则表达式相比:

Regex re = new Regex(@"^.*?(\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*|keyword1|keyword2|keyword3|keyword4|keyword5|keyword6|keyword7|keyword8|count:(\n\n?[0-9]?)?|keyword10|summary: \n).*$", 
    RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture);

有三个主要变化:

  • 我排除了领先和尾随.*
  • 我将前导者更改为.*?,使其变得非贪婪
  • 我在^模式中添加了行首和行尾锚($Multiline

现在它只对每个段落进行一次匹配尝试(通过或失败),并且它几乎从不回溯。如果我对您的数据有更多了解,我可能会更有效率。例如,如果每个关键字/标记/以字母开头的任何内容,字边界都会产生明显的效果(例如^.*?\b(\w+...)。

ExplicitCapture选项使所有“裸”组((...))充当非捕获组((?:...)),在不增加正则表达式混乱的情况下减少开销。如果要捕获令牌,只需将第一个组更改为命名组(例如(?<token>\w+...)。

答案 1 :(得分:1)

首先,您可以尝试编译选项RegexOptions.Compiled

以下是有关正则表达式效果的好文章:regex-performance

第二个:正则表达式的表现取决于模式: 有些模式比其他模式快得多。你必须尽可能严格地指定模式。

第三个。 我在使用正则表达式方面遇到了一些麻烦。在那种情况下,我使用了string.contains方法。 例如:

bool IsSomethingValid(stging source, string shouldContain, string pattern)
{
    bool res = source.Contains(shouldContain);
    if (res)
    {
         var regex = new Regex(pattern, RegexOptions.Compiled);
         res = regex.IsMatch(source);
    }
    return res;
}

如果您向我们提供了脚本示例,我们可能会尝试改进它们。

答案 2 :(得分:1)

永远不要假设您的应用程序运行缓慢的原因和原因。相反,始终衡量它。

使用体面的性能分析器(例如Red Gate's ANTS Performance Profiler - 他们提供14天免费试用版)并实际看到性能瓶颈。

我自己的经验是,我总是错误的是造成糟糕表现的真正原因。在分析的帮助下,我可以找到慢速代码段并调整它们以提高性能。

如果探查器确认您对正则表达式的假设,那么您可以尝试优化它们(通过修改或预编译它们)。

答案 3 :(得分:1)

如果模式不处理大小写...没有ignore case选项。见

Want faster regular expressions? Maybe you should think about that IgnoreCase option...

否则如上所述,但放在这里:

  1. 将所有操作放在单独的后台线程上。不要举起gui。
  2. 检查每个模式。对于*(零到多)的许多用法,如果交换到+(一对多)可以给正则表达式引擎一个真正的大提示,并且不需要那么多无结果回溯。见(Backtracking
  3. 编译模式可能需要一些时间,使用Compile选项可以避免再次解析...但是如果使用静态方法调用正则表达式解析器,则会自动缓存模式。
  4. 有关其他一些好东西,请参阅Regex hangs with my expression [David Gutierrez]