正则表达式性能随着时间的推移而降低

时间:2013-04-17 12:30:36

标签: c# regex

我写了一个简单的测试应用程序来检查使用正则表达式满足我的需求的可能性。我需要在提供的文本文件中找到并替换所有重复的标签,并使用一些唯一的字符串。例如,如果某些文本将在输入文件中找到更多,那么所有出现的文本应该替换为{1},依此类推。

为此,我创建了以下代码段:

    static void Main(string[] args)
    {
        StringBuilder xml = new StringBuilder(File.ReadAllText(@"C:\Integration\Item-26 - Copy.xml"));

        Regex r = new Regex(
            @"(?<exp>\<(?<tag>[^\<\>\s]+)[^\<\>]*\>[^\<\>]+\<\/\k<tag>\>).*\k<exp>", 
            RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

        List<string> values = new List<string>();

        MatchCollection matches = r.Matches(xml.ToString());

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        while (matches.Count > 0)
        {
            foreach (Match m in matches)
            {
                string matchValue = m.Groups["exp"].Value;
                values.Add(matchValue);
                xml.Replace(matchValue, string.Concat("{" + (values.Count - 1) + "}"));
            }

            Console.WriteLine("Analyzed " + matches.Count + " matches, total replacements = " + values.Count);

            matches = r.Matches(xml.ToString());
        }

        stopwatch.Stop();

        Console.WriteLine("=============== " + stopwatch.Elapsed.TotalSeconds);
        Console.ReadLine();
    }

问题是如果我有一个大文件作为输入(> 1MB),那么每次查找匹配的调用都需要比之前更长的时间。在开始时,调用matches.Count需要0.3秒。经过100次迭代后,需要1分钟的时间。

我已经检查了测试应用程序的内存使用情况 - 如果没有任何实际增长,它几乎不会消耗任何内容。

造成这种情况的原因是什么?如何才能获得稳定的表现? 提前谢谢。

1 个答案:

答案 0 :(得分:1)

这就是我认为问题所在。你的正则表达式是:

@"(?<exp>\<(?<tag>[^\<\>\s]+)[^\<\>]*\>[^\<\>]+\<\/\k<tag>\>).*\k<exp>"

所以你正在寻找类似的东西:

<tag>stuff</tag>lots of stuff here<tag>stuff</tag>

在第一次迭代期间,当替换内部标记时,正则表达式会快速失败,因为标记靠得很近。但随着更多内部标签的替换,标签之间的空间也会增加。很快你就有了:

<tag>stuff</tag>hundreds of kilobytes<tag2>other stuff</tag2><tag>stuff</tag>

回溯开始杀了你。

我怀疑您可以使用.*替换.*?(或我之前建议的[^\<]*)来解决此问题。因为你知道当你找到一个<时,你要么找到了一个匹配,要么就是一个明确的失败。