字符串数组为大型多行条目抛出 OutOfMemoryException
本文关键字:OutOfMemoryException 数组 大型 字符串 | 更新日期: 2023-09-27 18:32:47
在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 个文本量,在新行处截断并重复,直到所有内容都已阅读,但这似乎很乏味。或者有没有办法为它分配更多内存?
谢谢加勒特
更新
多亏了阿提拉,我想出了这个,它似乎有效。谢谢
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);
更好的方法是一次提取和处理一行,并使用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 并continue
或break
使用 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();
Split
将不得不复制原始文本的内存需求,以及每行string
对象的开销。 如果这会导致内存问题,则处理输入的可靠方法是一次分析一行。
我想在
大型文本文件上执行此操作的唯一方法是手动打开文件并使用StreamReader
。下面是如何执行此操作的示例。
您可以通过一次为每行创建字符串来避免为所有行和数组创建字符串:
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;
}
另一方面,在本文中说,关键是"拆分"方法实现得很差。阅读它,并得出结论。
就像阿提拉说的,你必须逐行解析。