2012-04-30 41 views
2

在Windows Forms C#應用程序中,我有一個用戶粘貼日誌數據的文本框,並對它進行排序。我需要單獨檢查每一行,以便按新行分割輸入,但如果有大量行數超過100,000行,則會引發OutOfMemoryException。字符串數組爲大型多行條目拋出OutOfMemoryException

我的代碼如下所示:

StringSplitOptions splitOptions = new StringSplitOptions(); 
if(removeEmptyLines_CB.Checked) 
    splitOptions = StringSplitOptions.RemoveEmptyEntries; 
else 
    splitOptions = StringSplitOptions.None; 

List<string> outputLines = new List<string>(); 

foreach(string line in input_TB.Text.Split(new string[] { "\r\n", "\n" }, splitOptions)) 
{ 
    if(line.Contains(inputCompare_TB.Text)) 
     outputLines.Add(line); 
} 
output_TB.Text = string.Join(Environment.NewLine, outputLines); 

的問題來自當我分裂一行文本框中的文字,這裏input_TB.Text.Split(new string[] { "\r\n", "\n" }

有沒有更好的方式來做到這一點?我已經考慮過採用第一個X數量的文本,截斷一個新行並重復,直到所有內容都被讀取,但這似乎很乏味。或者有沒有辦法爲它分配更多的內存?

感謝, 加勒特

更新

感謝阿提拉,我想出了這一點,它似乎工作。由於

StringReader reader = new StringReader(input_TB.Text); 
string line; 
while((line = reader.ReadLine()) != null) 
{ 
    if(line.Contains(inputCompare_TB.Text)) 
     outputLines.Add(line); 
} 
output_TB.Text = string.Join(Environment.NewLine, outputLines); 

回答

2

更好的方式來做到這一點。將提取和一次處理一行,並使用StringBuilder創建結果:

StringBuilder outputTxt = new StringBuilder(); 
string txt = input_TB.Text; 
int txtIndex = 0; 
while (txtIndex < txt.Length) { 
    int startLineIndex = txtIndex; 
GetMore: 
    while (txtIndex < txt.Length && txt[txtIndex] != '\r' && txt[txtIndex] != '\n')) { 
    txtIndex++; 
    } 
    if (txtIndex < txt.Length && txt[txtIndex] == '\r' && (txtIndex == txt.Length-1 || txt[txtIndex+1] != '\n') { 
    txtIndex++; 
    goto GetMore; 
    } 
    string line = txt.Substring(startLineIndex, txtIndex-startLineIndex); 
    if (line.Contains(inputCompare_TB.Text)) { 
    if (outputTxt.Length > 0) 
     outputTxt.Append(Environment.NewLine); 
    outputTxt.Append(line); 
    } 
    txtIndex++; 
} 
output_TB.Text = outputTxt.ToString(); 

先發制人的評論:有人會反對goto - 但這裏有什麼需要,替代品的要複雜得多(例如REG EXP),或僞造轉到另一個環和continuebreak

使用StringReader分割線是一個更清潔的解決方案 ,但它沒有手樂都 \r\n\n作爲新行

StringReader reader = new StringReader(input_TB.Text); 
StringBuilder outputTxt = new StringBuilder(); 
string compareTxt = inputCompare_TB.Text; 
string line; 
while((line = reader.ReadLine()) != null) { 
    if (line.Contains(compareTxt)) { 
    if (outputTxt.Length > 0) 
     outputTxt.Append(Environment.NewLine); 
    outputTxt.Append(line); 
    } 
} 
output_TB.Text = outputTxt.ToString(); 
+0

我甚至不知道你可以在C#中使用goto語句,不要以爲我曾經使用過一個,因爲我是一個孩子玩pascal和基本的,有趣的。這似乎過於複雜,但看看我的問題的更新。 –

+0

我在回答結尾處添加了一個註釋 - 您的更新比較乾淨,但不能同時處理'\ r \ n'和'\ n'作爲結束行。如果你可以避免這種情況 - 那麼我仍然建議使用'StringBuilder',避免創建一個(大?)中間字符串列表。 – MiMo

+0

是的,'goto'可以在C#中使用,並且在這種情況下我謹慎使用它們。 – MiMo

3

Split將不得不重複存儲必要的原文,加上string對象的開銷每一行。如果這導致內存問題,處理輸入的可靠方法是一次解析一行。

+0

謝謝,看看我的更新,讓我知道,如果這是你的意思。我會很快回答這個問題,我只想看看其他一些想法。再次感謝! –

0

我想在大文本文件上這樣做的唯一方法是手動打開文件並使用StreamReaderHere是一個例子,如何做到這一點。

0

,可以避免通過每次創建用於每行一個字符串創建用於所有行和陣列字符串:

var eol = new[] { '\r', '\n' }; 

var pos = 0; 
while (pos < input.Length) 
{ 
    var i = input.IndexOfAny(eol, pos); 
    if (i < 0) 
    { 
     i = input.Length; 
    } 
    if (i != pos) 
    { 
     var line = input.Substring(pos, i - pos); 

     // process line 
    } 
    pos = i + 1; 
} 
0

在另一方面,In this article說,一點是,「分裂」的方法是實施不佳。閱讀並做出結論。

像阿蒂拉說,你必須逐行解析。

相關問題