String数组为大型多行条目抛出OutOfMemoryException

时间:2012-04-30 11:45:00

标签: c# winforms string out-of-memory

在Windows Forms C#应用程序中,我有一个用户粘贴日志数据的文本框,并对其进行排序。我需要单独检查每一行,所以我用新行分割输入,但是如果有很多行,大于100,000,那么它会抛出一个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量的文本,截断一个新的行并重复直到读完所有内容,但这看起来很乏味。或者有没有办法为它分配更多的内存?

谢谢, 盖瑞特

更新

感谢Attila,我想出了这个,似乎有效。感谢

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

5 个答案:

答案 0 :(得分:3)

Split必须复制原始文本的内存需求,以及每行string个对象的开销。如果这导致内存问题,处理输入的可靠方法是一次解析一行。

答案 1 :(得分: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),或者用另一个循环伪造goto {{1 }或continue

使用break拆分行是一个更清晰的解决方案,但它不会同时处理StringReader\r\n作为新行:< / p>

\n

答案 2 :(得分:0)

我想在大文本文件上执行此操作的唯一方法是手动打开文件并使用StreamReaderHere是如何执行此操作的示例。

答案 3 :(得分: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;
}

答案 4 :(得分:0)

另一方面,In this article表示关键是“分裂”方法执行不力。阅读它,并得出结论。

像阿提拉说的那样,你必须逐行解析。