【问题标题】:Large string regex replace performance in C#C# 中的大字符串正则表达式替换性能
【发布时间】:2015-02-20 11:14:26
【问题描述】:

我在 C# 中遇到正则表达式性能问题。

我需要替换一个非常大的字符串(270k 字符,不要问为什么..)。正则表达式匹配大约 3k 次。

private static Regex emptyCSSRulesetRegex = new Regex(@"[^\};\{]+\{\s*\}", RegexOptions.Compiled | RegexOptions.Singleline);

public string ReplaceEmptyCSSRulesets(string css) {
  return emptyCSSRulesetRegex.Replace(css, string.Empty);
}

我传递给方法的字符串如下所示:

.selector-with-statements{border:none;}.selector-without-statements{}.etc{}

目前,替换过程在 C# 中占用 1500ms,但当我在 Javascript 中执行完全相同的操作时,它只需要 100ms

我用来计时的Javascript代码:

console.time('reg replace');
myLargeString.replace(/[^\};\{]+\{\s*\}/g,'');
console.timeEnd('reg replace');

我还尝试通过以相反的顺序循环匹配并替换 StringBuilder 中的字符串来进行替换。这没有帮助。

在这种情况下,我对 C# 和 Javascript 之间的性能差异感到惊讶,我认为我做错了什么,但我什么都想不出来。

【问题讨论】:

  • 我认为用于 JS 和 C# 的正则表达式引擎是不同的。您不应该比较不同语言的功能。我不确定 C# 但在 Java 中,创建正则表达式(当你做 matches() 时)本身是一个耗时的过程。它涉及同步和其他耗时的东西。
  • 尝试将\s 更改为[\f\n\r\t\v\u00A0\u2028\u2029] 或使用RegexOptions.ECMAScript。在 JS 和 C# 中处理字符类的方式存在差异
  • @TheLostMind 我同意,但差别很大......所以这可能是我在 C# 中做错了什么的信号。我正试图找出究竟是什么。
  • @stribizhev 你认为正则表达式有问题吗?这与 javascript 正则表达式执行时间不对应。但是我做了一个只匹配而不替换的测试,这在 C# 中很快。

标签: c# regex performance replace


【解决方案1】:

我无法真正解释 Javascript 和 C# 之间的时间差异(*)。但是您可以尝试提高模式的性能(这会产生大量回溯):

private static Regex emptyCSSRulesetRegex = new Regex(@"(?<keep>[^};{]+)(?:{\s*}(?<keep>))?", RegexOptions.Compiled);

public string ReplaceEmptyCSSRulesets(string css) {
    return emptyCSSRulesetRegex.Replace(css, @"${keep}");
}

您的原始模式的一个问题是,当大括号不为空(或未填充空格)时,正则表达式引擎将继续测试开始大括号之前的每个位置(结果始终相同)。示例:使用字符串abcd{1234},您的模式将从a 开始测试,然后是b ...

我建议的模式会消耗abcd,即使它后面没有空大括号,所以bcd的位置没有经过测试。

abcd 被捕获在名为 keep 的组中,但当找到空大括号时,捕获组将被空捕获组覆盖。

您可以了解两种模式所需的步骤数(检查调试器)

original pattern

new pattern

注意:如果您将[^}{;]+ 括在一个原子组中,则可以改进您的原始模式。此更改会将所需的步数除以 2(与原来相比),但即使如此,由于前面解释的原因,步数仍然很高。

(*) javascript 正则表达式引擎可能足够聪明,不会重试所有这些位置,但这只是一个假设。

【讨论】:

  • 谢谢!您的正则表达式似乎有效。我也会在 Javascript 正则表达式中尝试一下
  • @WillemdeWit:您不能在 javascript 中使用相同的模式,因为它不支持命名捕获,但您可以使用回调函数编写类似的东西作为替换:str.replace(/[^};{]+({\s*})?/g, function (m, g1) { return (g1) ? '' : m; });
  • 原来也快了 10 倍。
  • 比以前快 10 倍?还是比 JS 中的前一个正则表达式快 10 倍?
  • @CasimiretHippolyte - 您能否将“原​​始模式”和“新模式”正则表达式带入答案中 - 外部链接不受欢迎,因为外部站点可能会更改,从而破坏有价值的答案SO社区。除了在答案中显示正则表达式之外,您当然还可以拥有链接
猜你喜欢
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 2017-02-04
  • 1970-01-01
相关资源
最近更新 更多