String.Replace()与StringBuilder.Replace()

时间:2011-06-29 17:04:21

标签: c# .net .net-4.0 replace

我有一个字符串,我需要用字典中的值替换标记。它必须尽可能高效。使用string.replace执行循环只会消耗内存(字符串是不可变的,请记住)。 StringBuilder.Replace()会更好,因为这是为了使用字符串操作吗?

我希望避免花费RegEx,但如果这样做会更有效,那么就这样吧。

注意:我不关心代码复杂性,只关心它运行的速度和消耗的内存。

平均统计数据:长度为255-1024个字符,字典中为15-30个字符。

9 个答案:

答案 0 :(得分:67)

使用以下代码使用RedGate Profiler

class Program
    {
        static string data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
        static Dictionary<string, string> values;

        static void Main(string[] args)
        {
            Console.WriteLine("Data length: " + data.Length);
            values = new Dictionary<string, string>()
            {
                { "ab", "aa" },
                { "jk", "jj" },
                { "lm", "ll" },
                { "yz", "zz" },
                { "ef", "ff" },
                { "st", "uu" },
                { "op", "pp" },
                { "x", "y" }
            };

            StringReplace(data);
            StringBuilderReplace1(data);
            StringBuilderReplace2(new StringBuilder(data, data.Length * 2));

            Console.ReadKey();
        }

        private static void StringReplace(string data)
        {
            foreach(string k in values.Keys)
            {
                data = data.Replace(k, values[k]);
            }
        }

        private static void StringBuilderReplace1(string data)
        {
            StringBuilder sb = new StringBuilder(data, data.Length * 2);
            foreach (string k in values.Keys)
            {
                sb.Replace(k, values[k]);
            }
        }

        private static void StringBuilderReplace2(StringBuilder data)
        {
            foreach (string k in values.Keys)
            {
                data.Replace(k, values[k]);
            }
        }
    }
  • String.Replace = 5.843ms
  • StringBuilder.Replace#1 = 4.059ms
  • Stringbuilder.Replace#2 = 0.461ms

字符串长度= 1456

stringbuilder#1在方法中创建了stringbuilder,而#2则没有,所以性能差异最终会变得相同,因为你只是将该工作移出方法。如果你从stringbuilder而不是字符串开始,那么#2可能是你要去的方式。

就内存而言,使用RedGateMemory profiler,除非你进入很多整体上会赢得stringbuilder的替换操作,否则没有什么可担心的。

答案 1 :(得分:9)

这可能会有所帮助:

http://blogs.msdn.com/b/debuggingtoolbox/archive/2008/04/02/comparing-regex-replace-string-replace-and-stringbuilder-replace-which-has-better-performance.aspx

简短的回答似乎是String.Replace更快,尽管它可能会对你的内存占用/垃圾收集开销产生更大的影响。

答案 2 :(得分:6)

是的,StringBuilder会让你获得速度和内存(主要是因为每次你用它操作时它都不会创建一个字符串实例 - StringBuilder总是在同一个对象)。这是一个MSDN link,其中包含一些细节。

答案 3 :(得分:5)

  

stringbuilder.replace会比String.Replace更好吗

是的,好多了。如果你可以估计新字符串的上限(看起来你可以),那么它可能足够快。

当您创建它时:

  var sb = new StringBuilder(inputString, pessimisticEstimate);

然后StringBuilder将不必重新分配其缓冲区。

答案 4 :(得分:1)

将数据从String转换为StringBuilder并返回将需要一些时间。如果只执行单个替换操作,则此时间可能无法通过StringBuilder中固有的效率改进来补偿。另一方面,如果将字符串转换为StringBuilder,然后对其执行许多Replace操作,并在最后将其转换回来,则StringBuilder方法会更快。

答案 5 :(得分:1)

不是在整个字符串上运行15-30次替换操作,而是使用类似trie数据结构的东西来保存字典可能更有效。然后,您可以遍历输入字符串一次,以进行所有搜索/替换。

答案 6 :(得分:1)

很大程度上取决于给定字符串中平均存在多少个标记。

在StringBuilder和String之间搜索键的性能可能类似,但如果必须在单个字符串中替换多个标记,StringBuilder将会获胜。

如果你只希望每个字符串平均有一个或两个标记,并且你的字典很小,我会选择String.Replace。

如果有许多标记,您可能需要定义自定义语法来识别标记 - 例如用括号中适当的转义规则封装在括号中。然后,您可以实现一个解析算法,该算法迭代字符串的字符一次,识别并替换它找到的每个标记。或者使用正则表达式。

答案 7 :(得分:1)

我的两分钱,我只是编写了几行代码来测试每种方法的执行方式,并且正如预期的那样,结果是&#34;它取决于&#34;。

对于较长的字符串,Regex似乎表现得更好,对于较短的字符串,String.Replace就是这样。我可以看到StringBuilder.Replace的用法不是很有用,如果使用错误,它可能在GC透视图中是致命的(我试图共享StringBuilder的一个实例。

检查我的StringReplaceTests GitHub repo

答案 8 :(得分:1)

@ DustinDavis的问题&#39;答案是它以递归方式运行在同一个字符串上。除非您计划进行前后类型的操作,否则在这种测试中,每个操作案例都应该有单独的对象。

我决定创建自己的测试,因为我在整个网络上发现了一些相互矛盾的答案,我想完全确定。我正在处理的程序涉及大量文本(在某些情况下有数万行的文件)。

所以这是一个快速的方法,你可以复制和粘贴,看看自己哪个更快。您可能需要创建自己的文本文件进行测试,但您可以轻松地从任何地方复制和粘贴文本,并为自己制作足够大的文件:

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;

void StringReplace_vs_StringBuilderReplace( string file, string word1, string word2 )
{
    using( FileStream fileStream = new FileStream( file, FileMode.Open, FileAccess.Read ) )
    using( StreamReader streamReader = new StreamReader( fileStream, Encoding.UTF8 ) )
    {
        string text = streamReader.ReadToEnd(),
               @string = text;
        StringBuilder @StringBuilder = new StringBuilder( text );
        int iterations = 10000;

        Stopwatch watch1 = new Stopwatch.StartNew();
        for( int i = 0; i < iterations; i++ )
            if( i % 2 == 0 ) @string = @string.Replace( word1, word2 );
            else @string = @string.Replace( word2, word1 );
        watch1.Stop();
        double stringMilliseconds = watch1.ElapsedMilliseconds;

        Stopwatch watch2 = new Stopwatch.StartNew();
        for( int i = 0; i < iterations; i++ )
            if( i % 2 == 0 ) @StringBuilder = @StringBuilder .Replace( word1, word2 );
            else @StringBuilder = @StringBuilder .Replace( word2, word1 );
        watch2.Stop();
        double StringBuilderMilliseconds = watch1.ElapsedMilliseconds;

        MessageBox.Show( string.Format( "string.Replace: {0}\nStringBuilder.Replace: {1}",
                                        stringMilliseconds, StringBuilderMilliseconds ) );
    }
}

我得到了那个字符串。每次交换8到10个字母单词时,它的速度提高了大约20%。如果您需要自己的经验证据,请亲自尝试。