大字符串正则表达式替换C#中的性能

时间:2015-02-20 11:14:27

标签: c# regex performance replace

我在C#中遇到了Regex性能问题。

我需要更换一个非常大的字符串(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在这种情况下的性能差异感到惊讶,我认为我做错了什么但我想不出任何事情。

1 个答案:

答案 0 :(得分:2)

我无法真正解释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正则表达式引擎可能足够智能,无法重试所有这些位置,但这只是一个假设。