簡體   English   中英

為什么Regex和StringBuilder刪除空白的速度較慢?

[英]Why is Regex and StringBuilder slower at removing white space?

我們正在與外部API集成通信。 到目前為止,由於命名不一致,文檔不正確以及響應/錯誤消息不可靠,這讓人有些頭疼。

我們正在處理的事情之一是,我們發送給他們的某些請求對字符串的長度有限制。 沒有任何突破性的要求,但是任何包含任何超出長度要求的字符串的請求都將被拒絕並失敗。

我們的解決方案是為字符串創建一個擴展方法,該方法只采用最大長度,並返回從索引0開始的該長度的子字符串。

我是一名初級開發人員,這是我的第一份工作,所以我知道我的解決方案很可能不是最優雅或最有效的。 無論哪種方式,我都提出了一個要點,即由於我們當前的擴展名,我們可能最終會刪除相關的信息,同時包括可能毫無用處的空白,因為我們沒有進行修剪或做任何檢查雙倍空格的操作。等等。我的領導告訴我,隨時可以擴展名超載,您也可以選擇刪除空白。

我想出了3種解決方案,它們可以完全消除任何雙空格。 我知道Regex方法是唯一真正刪除所有空白的方法,而另兩個方法則是將所有出現的兩個空白都排回。 但是,此站點將僅在美國使用,因此我不確定是否需要額外花費Regex。

我發布此文章的主要興趣是,我想知道是否有人可以解釋為什么我使用StringBuilder的方法與其他兩個方法相比效率如此之低,甚至比Regex還要慢,我希望它是這三個方法中最快的。 這里的任何見解都受到贊賞,並暗示了比我提出的任何方法更好的方法。

這是我的三個擴展:

    public static string SafeSubstringSomehowTheQuickest(this string stringToShorten, int maxLength)
    {
        if (stringToShorten?.Length < maxLength || string.IsNullOrWhiteSpace(stringToShorten)) return stringToShorten;

        stringToShorten = stringToShorten.Trim();
        int stringOriginalLength = stringToShorten.Length;
        int extraWhitespaceCount = 0;
        for (int i = 0; i < stringOriginalLength - extraWhitespaceCount; i++)
        {
            int stringLengthBeforeReplace = stringToShorten.Length;
            stringToShorten = stringToShorten.Replace("  ", " ");
            if(stringLengthBeforeReplace < stringToShorten.Length) { extraWhitespaceCount += stringToShorten.Length - stringLengthBeforeReplace; } 
        }

        return stringToShorten.Length > maxLength ? stringToShorten.Substring(0, maxLength) : stringToShorten;
    }

    public static string SafeSubstringWithRegex(this string stringToShorten, int maxLength)
    {
        if (stringToShorten?.Length < maxLength || string.IsNullOrWhiteSpace(stringToShorten)) return stringToShorten;
        stringToShorten = System.Text.RegularExpressions.Regex.Replace(stringToShorten, @"\s{2,}", " ").Trim();

        return stringToShorten.Length > maxLength ? stringToShorten.Substring(0, maxLength) : stringToShorten;
    }

    public static string SafeSubstringFromBuilder(this string stringToShorten, int maxLength)
    {
        if (stringToShorten?.Length < maxLength || string.IsNullOrWhiteSpace(stringToShorten)) return stringToShorten;

        StringBuilder bob = new StringBuilder();
        bool lastCharWasWhitespace = false;

        foreach (char c in stringToShorten)
        {
            if (c == ' ' && !lastCharWasWhitespace) { bob.Append(c); }
            lastCharWasWhitespace = c == ' ';
            if (!lastCharWasWhitespace) { bob.Append(c); }
        }
        stringToShorten = bob.ToString().Trim();

        return stringToShorten.Length < maxLength ? stringToShorten : stringToShorten.Substring(0, maxLength);
    }

這是我用來比較每個擴展程序運行時間的快速測試:

    static void Main(string[] args)
    {
        var stopwatch = new System.Diagnostics.Stopwatch();

        string test =
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       " +
            "   foo bar   foobar           f    oo        bar foobar      foofoo                                            " +
            "barbar    foo b  ar                                                                                       ";

        int stringStartingLength = test.Length;
        int stringMaxLength = 30;

        stopwatch.Start();
        string somehowTheQuickestResult = test.SafeSubstringSomehowTheQuickest(stringMaxLength);
        stopwatch.Stop();
        var somehowTheQuickestResultTicks = stopwatch.ElapsedTicks;

        stopwatch.Start();
        string regexResult = test.SafeSubstringWithRegex(stringMaxLength);
        stopwatch.Stop();
        var regexResultTicks = stopwatch.ElapsedTicks;

        stopwatch.Start();
        string stringBuilderResult = test.SafeSubstringFromBuilder(stringMaxLength);
        stopwatch.Stop();
        var stringBuilderResultTicks = stopwatch.ElapsedTicks;
    }

最終,這些是結果,每次運行的滴答滴答都有所不同,但是這三種方法之間的差異是相當一致的:

這三個都返回相同的字符串:“ foo bar foobar f oo bar foobar”

SomehowTheQuickestResult(method 1):12840 ticks

regexResult(方法2): 14889次

stringBuilderResult(方法3): 15798次

您在進行基准測試時有些錯誤。

首先,您需要“熱身”並讓JIT做好工作。 基本上,只需調用您的三個方法並丟棄結果即可。

其次,單次嘗試不具有代表性。 嘗試超過100次或更多次迭代的平均時間(或中值時間)。

第三,您使用Stopwatch是錯誤的。 Stop() Start()之后的Start()恢復間隔測量。 Restart()是必經之路。 使用它,我的測試顯示以下結果:

9569
314
58

所以, StringBuilder方法實際上最快的國家之一。

暫無
暫無

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

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