2016-07-28 54 views
1

我們正在將通信與外部API集成。到目前爲止,由於命名不一致,文檔較差以及不可靠的響應/錯誤消息,這一直是一件令人頭痛的事情。爲什麼正則表達式和StringBuilder在刪除空白處速度較慢?

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

我們的解決方案是創建字符串的擴展方法,只是發生在最大長度和索引返回長度開始的子串0

我是一個初中dev的,這是我的第一份工作,所以我知道我的解決方案可能不是最優雅或高效的。無論哪種方式,我提出了這樣一個觀點,即在我們目前的擴展中,我們最終可能會刪除相關信息,同時包括潛在的毫無價值的空白空間,因爲我們沒有修整或做任何事情來檢查雙空格等。我的領導告訴我可以自由地做出擴展的過載可以讓你選擇去除空白。

我想出了3種解決方案,可以完全消除任何雙重空間。我意識到Regex方法是唯一一個真正去除所有空白區域的方法,其中另外兩個方法將兩個空格緊接着排除。然而,這個網站將在美國使用,所以我不確定是否需要額外的正則表達式時間。

我在發佈這個主要興趣是我想知道是否有人可以解釋爲什麼我的方法使用StringBuilder比其他兩個效率低,它甚至比正則表達式慢,我預計它是三者中最快的。這裏的任何見解都是值得讚賞的,同時也暗示了可能比我提出的任何更好的方法。

這裏是我的三個分機:

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

最後這些都是結果,每次運行時刻度會有所不同,但三種方法之間的差異相當一致:

所有三個方法返回相同的字符串的: 「富酒吧foobar的˚FOO酒吧foobar的」

somehowTheQuickestResult(方法1):12840蜱

regexResult(方法2):14889蜱

stringBuilderResult(方法3):15798 ticks

+0

如果你使用了一個字符數組,並且將非空白字符移動過來,它可能會更快。對於較大的字符串比較,您可能會得到與當前方法截然不同的結果 – BugFinder

+0

作爲一個附註,所有方法會返回一串很大的空格,即使它太長了。 – GSerg

+1

對於正則表達式,您可能希望通過使用必需的[flags](https://msdn.microsoft.com/zh-cn/library/h5845fdz(v = vs)創建明確的「靜態只讀」實例來排除編譯時間0.110)的.aspx)。對於stringbuilder,您可能想要將'maxLength'作爲['capacity'](https://msdn.microsoft.com/en-us/library/h1h0a5sy(v = vs.110).aspx)來傳遞。 – GSerg

回答

5

你在做你的基準測試有點不對。

首先,你需要「熱身」,讓JIT做好工作。基本上,只需調用你的三種方法並放棄結果。

接下來,單次嘗試並不具有代表性。嘗試平均(或中位時間)超過100次或更多的迭代。

三,您的使用Stopwatch是錯誤的。 之後Start()恢復間隔測量。 Restart()是要走的路。有了它,我的測試顯示的結果如下:

9569 
314 
58 

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

+0

很棒!對不起,這是我第一次使用秒錶。打算重做這個,並在幾千個週期內獲得更好的平均值。 – WRP