2

在 Windows 窗体 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 段文本,在新行处截断并重复,直到所有内容都已阅读完毕,但这似乎很乏味。或者有没有办法为它分配更多的内存?

谢谢,加勒特

更新

感谢阿提拉,我想出了这个,它似乎工作。谢谢

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

5 回答 5

3

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

于 2012-04-30T11:50:39.780 回答
2

更好的方法是一次提取和处理一行,并使用 aStringBuilder创建结果:

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 和continuebreak

使用 aStringReader拆分行是一种更清洁的解决方案,但它不能同时处理\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(); 
于 2012-04-30T12:11:05.523 回答
0

我想对大型文本文件执行此操作的唯一方法是手动打开文件并使用StreamReader. 是一个如何执行此操作的示例。

于 2012-04-30T11:51:06.487 回答
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;
}
于 2012-04-30T11:58:19.280 回答
0

另一方面,在这篇文章中,重点是“拆分”方法的实现很差。阅读它,并得出你的结论。

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

于 2012-04-30T11:58:36.107 回答