2016-11-22 52 views
0

所以我注意到操縱Strings是超慢的,當涉及到任何導致它調整大小,基本上刪除或添加字符(在我的情況下刪除)。使用固定字符[]複製數據並創建字符串,並不總是使用整個char [],安全嗎?

所以我想,使用stackalloc或修復臨時緩衝區,只是複製所有數據,除了我不希望等於刪除相同的東西。

但我需要爲這個緩衝區分配相同的長度,因爲這是極限,它永遠不會比它大,但它肯定會降低。

所以這裏是代碼,我不知道這樣做的方式實際上是否安全, 因爲可以有很多從未使用的緩衝區。

//Remove all unneccessery empty spaces 
    private unsafe static string FormatCodeUnsafe(string text) 
    { 

     int length = text.Length; 
     var charbuffer = new char[length]; 
     int index = 0; 
     fixed (char* charbuf = charbuffer) 
     fixed (char* strptr = text) 
     { 
      char* charptr = charbuf; 
      for (int i = 0; i < length; i++) 
      { 
       char c = strptr[i]; 

       if (i > 0) 
       { 
        if (c == ' ' && strptr[i - 1] == ',') 
         continue; 

        if (c == ' ' && strptr[i - 1] == ')') 
         continue; 
        if (c == ' ' && strptr[i - 1] == ' ') 
         continue; 
       } 
       if (i < length - 1) 
       { 
        if (c == ' ' && strptr[i + 1] == ' ') 
         continue; 
        if (c == ' ' && strptr[i + 1] == ',') 
         continue; 
        if (c == ' ' && strptr[i + 1] == '(') 
         continue; 
       } 

       *charptr = c; 
       charptr++; 
       index++; 
      } 
     } 
     //Return the result 
     return new string(charbuffer, 0, index); 
    } 

編輯:

硬答案既是之間做出選擇給予很好的例子和說明。 我想選擇兩種幫助,但我不得不選擇一個。 !

謝謝:)

+0

我認爲如果輸入包含連續的空格,您的代碼不會給出所需的結果,因爲它們將被全部刪除。因此,如果單詞之間有多個空格,'abc def'將返回'abcdef'。 – Phil1970

回答

1

操縱strings是緩慢的,因爲字符串是不可變的 - 每次加連擊或更換新的字符串被創建的字符串的部分時間。

因爲string操作是很常見的,有在.NET Framework另一個類 - StringBuilder,它允許你這樣做非常有效(這是可變),當你完成,你可以得到通過在StringBuilder實例上調用ToString()方法產生string

您的代碼看起來是這樣的:

private static readonly char[] SkipCharacters = new[] {',', '(', ')'}; 

//Remove all unneccessery empty spaces 
private static string FormatCode(string text) 
{ 
    StringBuilder builder = new StringBuilder(); 
    for (int i = 0; i < text.Length; i++) 
    { 
     var character = text[i]; 
     //set defaults - so that we do not have to check 
     //for the start and end of the string 
     char previous = 'x'; 
     char next = 'x'; 
     if (i > 0) 
     { 
      previous = text[i - 1]; 
     } 
     if (i < text.Length - 1) 
     { 
      next = text[i + 1]; 
     } 
     if (character == ' ' && 
       SkipCharacters.Contains(previous) || 
       SkipCharacters.Contains(next)) 
     { 
      continue; 
     } 
     builder.Append(character); 
    } 
    return builder.ToString(); 
} 

使用不安全代碼可能會比這個管理辦法快一點,但事實的性能增益阻礙,你可能是浪費很多空間(用於整個文本大小的陣列)並且正在使用潛在危險較少維護代碼。也就是說,如果你的基準測試結果顯示unsafe的性能顯着提升,那麼如果你小心:-),沒有什麼能夠阻止你使用它。

+0

我知道Stringbuilder,但是能否詳細說明如何使用我的方式可能會有危險。 如果我不使用整個緩衝區會發生什麼情況,它會卡在某個地方,還是隻在函數之後清理? – Zerowalker

+2

方法完成後,未使用的空間肯定會被清理乾淨(感謝垃圾回收器),所以您不必擔心它會卡住。 「危險」僅僅是你需要使用'unsafe'這一事實,如果一個管理解決方案不會顯着較慢(這不應該與'StringBuilder'一起使用),這總是更好的避免。也就是說,如果你的基準測試顯示'unsafe'方法顯然更快,那麼肯定沒有什麼能夠阻止你使用它:-)。 –

+2

'FormatCodeUnsafe'應該重新命名,因爲它不再是'unsafe'! – Phil1970

1

那麼,如果我必須編寫代碼,我會做類似這樣的事情......確切的實現可能會有所不同,這取決於您想要如何處理連續的空格。這段代碼假設我們要修剪前後空格併合並內部空格。

我在代碼中加入了一些額外的解釋來幫助理解代碼。

string FormatCode(string input) 
{ 
    int indexSpace = input.IndexOf(' '); 

    if (indexSpace == -1) 
    { 
     // If the string does not contains any space, return it as it. 
     return input; 
    } 

    // The index from where we want to append data... 
    int index = 0; 

    // Preallocate memory using old size as the starting point... 
    var builder = new StringBuilder(input.Length); 

    while(true) 
    { 
     // Append everything before the current space... 
     builder.Append(input.Substring(index, indexSpace - index)); 

     // Decide if we want to keep that space... 

     // Do not keep initial spaces... 
     bool needSpace = indexSpace > 0; 

     if (needSpace) 
     { 
      // Do not keep space after selected symbols... 
      switch (input[indexSpace - 1]) 
      { 
       case ',': 
       case ')': 
        needSpace = false; 
        break; 
      } 
     } 

     // Find the next character that is not a space as we always want 
     // to merge consecutives spaces and detecting them help handle 
     // edge cases. 
     int indexNotSpace = indexSpace; 
     while (++indexNotSpace < input.Length && input[indexNotSpace] == ' ') 
     { 
     } 

     if (indexNotSpace == input.Length) 
     { 
      // The remaining of the string consist only of spaces... 
      break; 
     } 

     if (needSpace) 
     { 
      // Do not keep spaces before selected symbols... 
      switch (input[indexSpace + 1]) 
      { 
       case ',': 
       case '(': 
        needSpace = false; 
        break; 
      } 
     } 

     if (needSpace) 
     { 
      builder.Append(' '); 
     } 

     // Find next space not already processed... 
     index = indexNotSpace; 
     indexSpace = input.IndexOf(' ', index); 

     if (indexSpace == -1) 
     { 
      // There are not remaining space so append remaining text 
      // and exit loop. 
      builder.Append(input.Substring(index)); 
      break; 
     } 
    } 

    return builder.ToString(); 
} 

該代碼會得出不同的結果比在某些情況下,原來的代碼,但我認爲,原來的代碼不會在某些情況下給預期的結果。

讓我們用〜來表示空格。

input  : abc~~def 
OP output : abcdef 
My output : abc~def 

input  : ~abc~ 
OP output : ~abc~ 
My output : abc 

input  : ~~~(~~test~~)~~~ 
OP output : (test) 
My output : (~test~) 

input  : (~~~~~~) 
OP output :() 
My output : (~) 
+0

有趣的是,我認爲我的輸出是正確的(對於我的用例), 除了第二個例子,儘管在我的情況下我很少發現它。 好例子:) – Zerowalker

+0

那麼,'abc def'(一個空格)會保留空格,而'abc def'(兩個或更多空格)會將它們全部刪除,這會很奇怪。 – Phil1970

相關問題