【问题标题】:C# program freezes for a minute when running a bunch of RegExesC# 程序在运行一堆 RegEx 时冻结一分钟
【发布时间】:2012-01-07 23:25:46
【问题描述】:

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

每次完成后,我都觉得我在某处忘记了 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);

这是相当大的,毫无疑问。这个想法是,如果它到达任何关键字或链接,它将只删除围绕它的整个段落。

【问题讨论】:

  • 如果它冻结了 gui,你应该在后台线程上运行这些 RegExes。
  • 正则表达式可能很昂贵。他们的语法可以给人一种他们正在发挥魔力的错觉,但在幕后他们并不总是有效的。如果可能,您应该Compile 正则表达式以提高性能。
  • 您可以尝试使用Parallel.ForParallel.ForEachParallel.Invoke(在System.Threading.Tasks 命名空间下)并行化您的代码
  • 那么……正则表达式在哪里?哪些在哪些词集上较慢? (5、10、15 和 1000 是小数字)。剖析显示什么? (10 * 15 是相对较少的运行次数。)
  • 向我们展示您的正则表达式和代码。我们无法在没有看到他们的情况下回答您的问题。运行 10 个正则表达式,每个 15 次应该很快。但是,如果您的表达结构不佳,则可能需要非常非常长的时间。

标签: c# regex performance


【解决方案1】:

正则表达式不会杀死 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 选项使所有“裸”组 ((...)) 像非捕获组 ((?:...)) 一样,在不增加正则表达式混乱的情况下进一步减少开销。如果您想捕获令牌,只需将第一个组更改为命名组(例如(?&lt;token&gt;\w+...)。

【讨论】:

  • 感谢新人的启发 :)
【解决方案2】:

首先你可以试试编译选项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;
}

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

【讨论】:

    【解决方案3】:

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

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

    我自己的经验是,我总是弄错导致表现不佳的真正原因。在 profiling 的帮助下,我可以找到缓慢的代码段并对其进行调整以提高性能。

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

    【讨论】:

      【解决方案4】:

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

      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] 了解其他一些好东西。

      【讨论】:

        猜你喜欢
        • 2021-12-04
        • 1970-01-01
        • 1970-01-01
        • 2021-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-08
        • 2022-11-07
        相关资源
        最近更新 更多