簡體   English   中英

String.Replace() 與 StringBuilder.Replace()

[英]String.Replace() vs. StringBuilder.Replace()

我有一個字符串,我需要用字典中的值替換標記。 它必須盡可能高效。 使用 string.replace 循環只會消耗 memory (字符串是不可變的,請記住)。 StringBuilder.Replace() 會更好嗎,因為它是為字符串操作而設計的?

我希望避免 RegEx 的費用,但如果這樣會更有效率,那就這樣吧。

注意:我不關心代碼的復雜性,只關心它的運行速度和它消耗的 memory。

平均統計數據:長度為 255-1024 個字符,字典中有 15-30 個鍵。

使用以下代碼使用 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 而不是 string 開始,那么 #2 可能是 go 的方式。

就 memory 而言,使用 RedGateMemory 分析器,沒有什么可擔心的,直到你進入許多替換操作,其中 stringbuilder 將贏得整體。

這可能會有所幫助: https://docs.microsoft.com/en-us/archive/blogs/debuggingtoolbox/comparing-regex-replace-string-replace-and-stringbuilder-replace-which-has-better-performance

簡短的回答似乎是 String.Replace 更快,盡管它可能會對您的 memory 占用空間/垃圾收集開銷產生更大的影響。

是的, StringBuilder會給您帶來速度和 memory 的增益(基本上是因為它不會在您每次使用它進行操作時創建字符串的實例 - StringBuilder始終使用相同的對象進行操作)。 這是一個包含一些詳細信息的MSDN 鏈接

stringbuilder.replace 會更好[比 String.Replace]

是的,好多了。 如果您可以估計新字符串的上限(看起來可以),那么它可能會足夠快。

當你像這樣創建它時:

  var sb = new StringBuilder(inputString, pessimisticEstimate);

那么 StringBuilder 將不必重新分配其緩沖區。

我的兩分錢在這里,我只寫了幾行代碼來測試每種方法的執行方式,並且正如預期的那樣,結果是“取決於”。

對於較長的字符串, Regex似乎表現更好,對於較短的字符串, String.Replace它是。 我可以看到StringBuilder.Replace的使用不是很有用,如果使用不當,從 GC 的角度來看它可能是致命的(我試圖分享一個StringBuilder的實例)。

檢查我的StringReplaceTests GitHub repo

@DustinDavis 的答案的問題是它遞歸地對同一個字符串進行操作。 除非您打算進行來回類型的操作,否則在這種測試中,您確實應該為每個操作案例設置單獨的對象。

我決定創建自己的測試,因為我在 Web 中發現了一些相互矛盾的答案,我想完全確定。 我正在處理的程序處理大量文本(在某些情況下,文件有數萬行)。

所以這里有一個快速的方法,您可以復制和粘貼並自己查看哪個更快。 您可能必須創建自己的文本文件進行測試,但您可以輕松地從任何地方復制和粘貼文本,並為自己制作一個足夠大的文件:

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 ) );
    }
}

我得到了那個 string.Replace() 每次換出 8-10 個字母的單詞時,速度提高了大約 20%。 如果您想要自己的經驗證據,請親自嘗試。

將數據從 String 轉換為 StringBuilder 並返回需要一些時間。 如果只執行一次替換操作,那么這個時間可能無法通過 StringBuilder 中固有的效率改進來彌補。 另一方面,如果將字符串轉換為 StringBuilder,然后對其執行多次替換操作,最后再將其轉換回來,則 StringBuilder 方法往往更快。

與其對整個字符串運行 15-30 次替換操作,不如使用trie數據結構之類的東西來保存字典可能更有效。 然后,您可以遍歷輸入字符串一次以進行所有搜索/替換。

這在很大程度上取決於給定字符串中平均存在多少個標記。

在 StringBuilder 和 String 之間搜索鍵的性能可能相似,但如果您必須替換單個字符串中的許多標記,則 StringBuilder 會勝出。

如果您平均每個字符串只期望一個或兩個標記,並且您的字典很小,那么我只會 go 用於 String.Replace。

如果有很多標記,您可能需要定義自定義語法來識別標記 - 例如,使用適合文字大括號的 escaping 規則將其括在大括號中。 然后,您可以實現一個解析算法,該算法遍歷字符串的字符一次,識別並替換它找到的每個標記。 或者使用正則表達式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM