【问题标题】:Whats the best way to optimise my regex matching什么是优化我的正则表达式匹配的最佳方法
【发布时间】:2013-10-23 04:20:10
【问题描述】:

我有一个带有文本框的应用程序。用户在此框中输入文本。

我在该文本框中的 OnKeyUp 事件中触发了以下函数

private void bxItemText_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{               
    // rules is an array of regex strings
    foreach (string rule in rules)
    {
        Regex regex = new Regex(rule);
        if (regex.Match(text.Trim().ToLower()))
        {
            // matched rule is a variable
            matchedRule = rule;
            break;
        }
    }
}

我在rules 中有大约 12 个字符串,尽管这可能会有所不同。

一旦文本框中的文本长度超过 80 个字符,性能就会开始下降。在 100 个字符后键入一个字母需要一秒钟才能显示出来。

如何优化它?我应该在每 3 个 KeyUp 上匹配吗?我应该完全放弃 KeyUp 并每隔几秒钟自动匹配一次吗?

【问题讨论】:

  • rules 何时更改?它们可以合并成一个单一的正则表达式吗?如果没有,您可以在 KeyUp 上启动一个计时器,等待 300 毫秒,然后 然后 进行正则表达式测试。这将防止用户做得太快。匹配规则时会发生什么?这个活动可以延期吗?
  • 除了只编译一次表达式(规则)之外,如果您使用单个表达式而不是 ~12 会有所帮助。如果您不更换,那么将它们组合起来很简单。您也可以尝试使用(?>...) 尽可能消除回溯。
  • 正则表达式适用于回溯,因此您的输入越大延迟越大,您应该避免检查如此大的字符串,否则会影响您的代码停止响应。
  • 你必须使用正则表达式吗?根据您查看的匹配类型,您自己的简单扫描仪可能会更快
  • Rob:你能详细说明一下吗?目前我实际上只匹配大量关键字,我只是假设正则表达式是最快的方法。

标签: c# regex windows-phone-8


【解决方案1】:

如何优化它?我应该在每 3 个 KeyUp 上匹配吗?我应该完全放弃 KeyUp 并每隔几秒钟自动匹配一次吗?

我会选择第二个选项,即放弃 KeyUp 并每隔几秒触发一次验证,或者最好在 TextBox 失去焦点时触发验证。

另一方面,我应该建议预先缓存正则表达式并编译它们,因为您似乎在一遍又一遍地使用它们,换句话说,而不是将规则作为字符串存储在该数组中,您应该存储在添加或加载它们时将它们作为编译的正则表达式对象。

【讨论】:

    【解决方案2】:

    使用static方法调用而不是每次都创建一个新对象,static调用使用caching特性:Optimizing Regular Expression Performance, Part I: Working with the Regex Class and Regex Objects。

    这将是性能的重大改进,那么您可以提供您的正则表达式(规则),看看是否可以在正则表达式中进行一些优化。

    其他资源:

    【讨论】:

    • 在您链接到的页面底部,它指出最佳做法是使用已编译的正则表达式来优化预先知道的正则表达式模式的性能以及何时调用频率到正则表达式的模式匹配方法可以有很大的不同。这不符合OP的情况吗?
    • 您是在谈论使用RegexOptions.Compiled 标志编译的正则表达式还是使用Regex.CompileToAssembly 方法将正则表达式编译到另一个程序集?第一个可能比第二个更好,一般来说,将正则表达式编译为单独的程序集更适合库,因为维护它们的工作量很大。
    • 我的意思是使用RegexOptions.Compiled,无论是在我的评论中还是在我上面的回答中。
    • 好的,我明白了,我们说的不是同一件事。事实上,如果他经常使用相同的正则表达式,则可以使用RegexOptions.Compiled 选项并保留实例的引用。保持实例引用和静态调用之间的好处是静态调用执行的缓存查找。
    【解决方案3】:

    在正则表达式级别将字符串组合为一个比在代码中的 foreach 更快。 Combining two Regex to one

    【讨论】:

    • 它们不能合并。我需要知道哪个规则匹配。如果它们都是相同的规则,我无法区分它们
    【解决方案4】:

    如果您需要为每个新符号确定模式,并且您关心性能,那么最终状态机似乎是最佳选择... 那是更难的方式。您应该为每个符号指定允许的下一个符号的列表。 如果可能的话,OnKeyUp 你只​​需进入下一个状态。您将拥有输入文本当前匹配的模式数量。 我可以找到一些有用的参考资料:

    FSM example

    Guy explaining how to convert Regex to FSM

    Regex - FSM converting discussion

    【讨论】:

      【解决方案5】:

      您不需要每次都创建一个新的正则表达式对象。如果之前使用过(从 .Net 2 开始),也使用静态调用将缓存模式。这是我将如何重写它

      matchedRule = Rules.FirstOrDefault( rule => Regex.IsMatch(text.Trim().ToLower(), rule));

      【讨论】:

        【解决方案6】:

        鉴于您似乎在匹配关键字,您能否只对已编辑文本的当前部分(即光标附近)执行匹配?设计起来可能会很棘手,尤其是对于粘贴或撤消等操作,但有很大的性能提升空间。

        【讨论】:

          【解决方案7】:

          预编译你的正则表达式(使用RegexOptions.Compiled)。另外,您可以通过扩展您的正则表达式使Trim 和ToLower 冗余吗?您为每个规则运行一次 Trim 和 ToLower,即使您无法完全消除它们,这也是低效的

          您可以尝试使您的规则互斥 - 这应该会加快速度。我做了一个简短的测试:匹配以下内容

          “猫|汽车|出租车|盒子|气球|按钮”

          这样写可以加快速度

          “ca(t|r|b)|b(ox|alloon|utton)”

          【讨论】:

          • 在这种情况下,预编译不会很有用,甚至会变得更糟,因为他每次都使用实例方法并创建一个新对象。如果您谈论的是在设计时编译每条规则,这将有所帮助,但需要额外的工作来在运行时为每条规则选择正确的类。为了使用caching,静态调用将是一个更好的选择:blogs.msdn.com/b/bclteam/archive/2010/06/25/…
          • @polkduran:嗯? OP 在某处有一组规则,目前表示为字符串。如果他用一组预编译的正则表达式替换它,他将获得显着的性能提升(在我公认的微不足道的测试中,性能提升了 50%)。我错过了什么?
          • 我认为在设计时将正则表达式列表编译到另一个程序集、将程序集添加到应用程序、在运行时将规则映射到实例类并在每次新规则时维护所有这些的额外工作需要添加将需要大量工作才能获得性能。静态调用保留已编译正则表达式的缓存(默认为 15,可以修改更改 Regex.CacheSize 的值),如果正则表达式不在缓存中,它将在运行时编译,但在第一次调用之后NFA 已经建成。
          猜你喜欢
          • 1970-01-01
          • 2011-04-14
          • 1970-01-01
          • 2010-10-14
          • 1970-01-01
          • 2023-02-25
          • 1970-01-01
          • 2021-12-11
          • 2017-03-17
          相关资源
          最近更新 更多