簡體   English   中英

字符串操作的更好選擇-.NET

[英]Better option for String Manipulation - .NET

我正在為C#中的項目處理巨大的string數據。 我對應該使用哪種方法來處理string數據感到困惑。

第一種方法:

StringBuilder myString = new StringBuilder().Append(' ', 1024);

while(someString[++counter] != someChar)
    myString[i++] += someString[counter];


第二種方法:

String myString = new String();

int i = counter;
while(soumeString[++counter] != someChar);
myString = someString.SubString(i, counter - i);

兩者中哪一個會更快(和更有效)? 考慮到我正在使用的字符串很大。

字符串已經在RAM 字符串的大小可以從32MB-1GB不等。

您應該使用IndexOf而不是在循環中進行單獨的字符操作,然后將整個字符串塊添加到結果中:

StringBuilder myString = new StringBuilder();
int pos = someString.IndexOf(someChar, counter);
myString.Append(someString.SubString(counter, pos));

對於“巨大”的字符串,采取流式處理方法而不將整個內容加載到內存中可能是有意義的。 為了獲得最佳原始性能,有時可以使用指針數學來搜索和捕獲字符串片段,從而加快速度。

明確地說,我要說明兩種完全不同的方法。

1-流
OP並未說明這些字符串的大小,但是將它們加載到內存中可能不切實際。 也許正在從文件,從連接到DB的數據讀取器,從活動的網絡連接等讀取它們。

在這種情況下,我將打開一個流,向前讀取,將我的輸入緩沖在StringBuilder直到滿足條件為止。

2-不安全的字符處理
這就需要有完整的字符串。 您可以很簡單地在字符串開頭獲取char *:

// fix entire string in memory so that we can work w/ memory range safely
fixed( char* pStart = bigString ) 
{
    char* pChar = pStart; // unfixed pointer to start of string
    char* pEnd = pStart + bigString.Length;
}

現在,您可以遞增pChar並檢查每個字符。 您可以選擇是否緩沖它(例如,如果要檢查多個相鄰字符)。 一旦確定了結束存儲位置,便可以使用一系列數據。

C#中的不安全代碼和指針

2.1-更安全的方法

如果您熟悉不安全的代碼,它會非常快速,富有表現力並且非常靈活。 如果沒有,我仍然會使用類似的方法,但是沒有指針數學。 這類似於@supercat建議的方法,即:

  • 獲取一個字符[]。
  • 逐個字符地通讀它。
  • 在需要的地方緩沖。 StringBuilder對此StringBuilder 設置初始大小並重用實例。
  • 在需要的地方分析緩沖區。
  • 經常轉儲緩沖區。
  • 當緩沖區包含所需的匹配項時,對其進行處理。

對於不安全的代碼,有強制性的免責聲明:在大多數情況下,框架方法是更好的解決方案。 它們是安全的,經過測試的,每秒可調用數百萬次。 不安全的代碼使開發人員承擔全部責任。 它沒有做任何假設; 您要成為一個好的框架/ OS公民(例如,不覆蓋不可變的字符串,允許緩沖區溢出等)。 因為它沒有做任何假設並取消了保護措施,所以通常可以提高性能。 由開發人員確定是否確實有好處,並確定好處是否足夠顯着。

根據OP的要求,這是我的測試結果。

假設:

  • 大字符串已經在內存中,不需要從磁盤讀取
  • 目標是不使用任何本機指針/不安全塊
  • “檢查”過程非常簡單,因此不需要正則表達式之類的東西。 現在簡化為單個字符比較。 下面的代碼可以很容易地修改為一次考慮多個字符,這對兩種方法的相對性能沒有影響。

     public static void Main() { string bigStr = GenString(100 * 1024 * 1024); Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 10; i++) { int counter = -1; StringBuilder sb = new StringBuilder(); while (bigStr[++counter] != 'x') sb.Append(bigStr[counter]); Console.WriteLine(sb.ToString().Length); } sw.Stop(); Console.WriteLine("StringBuilder: {0}", sw.Elapsed.TotalSeconds); sw = Stopwatch.StartNew(); for (int i = 0; i < 10; i++) { int counter = -1; while (bigStr[++counter] != 'x') ; Console.WriteLine(bigStr.Substring(0, counter).Length); } sw.Stop(); Console.WriteLine("Substring: {0}", sw.Elapsed.TotalSeconds); } public static string GenString(int size) { StringBuilder sb = new StringBuilder(size); for (int i = 0; i < size - 1; i++) { sb.Append('a'); } sb.Append('x'); return sb.ToString(); } 

結果(發布版本,.NET 4):

StringBuilder〜7.9

子串〜1.9

StringBuilder始終慢> 3倍,並且使用各種大小不同的字符串。

有一個IndexOf操作可以更快地搜索someChar ,但是我假設您要查找所需長度的實際函數要比這復雜得多。 在這種情況下,我建議將someString復制到Char[]進行搜索,然后使用new String(Char[], Int32, Int32)構造函數生成最終的字符串。 Char[]進行索引比對StringStringBuilder索引要高效得多,除非您通常只需要字符串的一小部分,否則將所有內容復制到Char[]將是一個“勝利”。 '(當然,除非您可以簡單地使用IndexOf東西)。

即使字符串的長度通常比感興趣的長度大得多,使用Char[]還是最好的選擇。 Char[]預初始化為某個大小,然后執行以下操作:

Char[] temp = new Char[1024];
int i=0;
while (i < theString.Length)
{
  int subLength = theString.Length - i;
  if (subLength > temp.Length)  // May impose other constraints on subLength, provided
    subLength = temp.Length;    // it's greater than zero.
  theString.CopyTo(i, temp, 0, subLength);
  ... do stuff with the array
  i+=subLength;
}

完成所有操作后,您可以使用一個SubString調用來構造帶有原始字符中必要字符的字符串。 如果您的應用程序需要綁定字符與原始字符不同的字符串,則可以使用StringBuilder並在上述循環中使用Append(Char[], Int32, Int32)方法向其中添加已處理的字符。

還要注意的是,當使用上述循環構造時,只要不將subLength減小subLength ,就可以決定在循環中的任何點減小subLength 例如,如果試圖查找字符串是否包含用括號括起來的十六進制或更少的質數,則可以從掃描開放式括號開始; 如果找到它,並且正在尋找的數據可能會超出數組,請將subLength設置為開放父級的位置,然后重新循環。 這種方法將導致少量的冗余復制,但不會很多(通常沒有),並且消除了跟蹤循環之間的解析狀態的需要。 一個非常方便的模式。

在處理字符串時,您總是想使用StringBuilder。 因為字符串是不可變的,所以每次需要創建一個新對象時。

暫無
暫無

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

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