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

本文关键字:RegExes 一堆 一分钟 冻结 程序 运行 | 更新日期: 2023-09-27 18:19:47

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

每次做完,我都觉得自己忘了一根线。睡在那里的某个地方(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);

毫无疑问,它非常巨大。其想法是,如果它访问了任何关键字或链接,它只会删除围绕它的整个段落。

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

regex不会杀死CPU,regex作者会杀死CPU。;)

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

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

当所有分支都报告了故障时,regex引擎会前进一个位置,然后再次遍历所有分支。这是十几个分支,乘以段落中的字符数,乘以段落数。。。我想你明白了。将其与此正则表达式进行比较:

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+...)。

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

以下是关于regex性能的好文章:regex性能

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

还有第三个。我在正则表达式性能方面遇到了一些问题。在这种情况下,我使用了string.contels方法。例如:

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;
}

如果你给我们举一个你的剧本的例子,我们可能会尝试改进它们。

永远不要假设应用程序速度慢的原因。相反,总是测量它。

使用一个不错的性能评测器(比如红门的ANTS性能评测器,他们提供14天的免费试用版),并实际查看性能瓶颈。

我自己的经验是,我总是错误地认为什么是导致糟糕表现的真正原因。在评测的帮助下,我可以找到速度较慢的代码段,并对它们进行调整以提高性能。

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

如果模式不处理case。。。不要有忽略大小写选项。参见

想要更快的正则表达式吗?也许你应该考虑IgnoreCase选项。。。

如前所述,但放在这里:

  1. 将所有操作放在单独的后台线程上。不要举起gui
  2. 检查每个图案。对于*(零对多)的许多用法,如果换成+(一对多),可以给正则表达式引擎一个真正的大提示,而不需要太多的无结果回溯。请参阅(回溯)
  3. 编译模式可能需要时间,请使用"编译"选项以避免再次解析它。。。但是,如果使用静态方法来调用regex解析器,则模式会自动缓存
  4. 看到Regex挂在我的表情[大卫·古铁雷斯]上寻找其他好东西