C#错误:OutOfMemoryException - 读取大文本文件并从字典中替换

Tan*_*ngo 0 c# dictionary system.io.file

我是C#和面向对象编程的新手.我有一个解析文本文件的应用程序.

该应用程序的目标是读取提供的文本文件的内容并替换匹配的值.

当提供大约800 MB到1.2 GB的文件作为输入时,应用程序崩溃并出现错误System.OutofMemoryException.

在研究方面,我遇到了几个建议改变目标平台的答案:x64.

更改目标平台后存在同样的问题.

以下是代码:

// Reading the text file
                var _data = string.Empty;
                using (StreamReader sr = new StreamReader(logF))
                {
                    _data = sr.ReadToEnd();
                    sr.Dispose();
                    sr.Close();
                }

                foreach (var replacement in replacements)
                {
                    _data = _data.Replace(replacement.Key, replacement.Value);
                }


                //Writing The text File
                using (StreamWriter sw = new StreamWriter(logF))
                {
                    sw.WriteLine(_data);
                    sw.Dispose();
                    sw.Close();
                } 
Run Code Online (Sandbox Code Playgroud)

错误指向

_data = sr.ReadToEnd();

替换是一本字典.Key包含原始单词,Value包含要替换的单词.

Key元素将替换为KeyValuePair的Value元素.

接下来的是阅读文件,替换和写作.

我尝试使用StringBuilder而不是字符串,但应用程序崩溃了.

这可以通过一次读取一行文件,替换和写入来克服吗?做同样的事情的有效和快捷方式是什么.

更新:系统内存为8 GB,在监控性能时,内存占用率达到100%.

@Tim Schmelter的答案很有效.

但是,内存利用率飙升超过90%.这可能是由于以下代码:

            String[] arrayofLine = File.ReadAllLines(logF);
            // Generating Replacement Information
            Dictionary<int, string> _replacementInfo = new Dictionary<int, string>();
            for (int i = 0; i < arrayofLine.Length; i++)
            {
                foreach (var replacement in replacements.Keys)
                {
                    if (arrayofLine[i].Contains(replacement))
                    {
                        arrayofLine[i] = arrayofLine[i].Replace(replacement, masking[replacement]);
                        if (_replacementInfo.ContainsKey(i + 1))
                        {
                            _replacementInfo[i + 1] = _replacementInfo[i + 1] + "|" + replacement;
                        }
                        else
                        {
                            _replacementInfo.Add(i + 1, replacement);
                        }
                    }
                }
            }

//Creating Replacement Information
                StringBuilder sb = new StringBuilder();
                foreach (var Replacement in _replacementInfo)
                {
                    foreach (var replacement in Replacement.Value.Split('|'))
                    {
                        sb.AppendLine(string.Format("Line {0}: {1} ---> \t\t{2}", Replacement.Key, replacement, masking[replacement]));
                    }
                }

                // Writing the replacement information
                if (sb.Length!=0)
                { 
                using (StreamWriter swh = new StreamWriter(logF_Rep.txt))
                {
                    swh.WriteLine(sb.ToString());
                    swh.Dispose();
                    swh.Close();
                }
                }
                sb.Clear();
Run Code Online (Sandbox Code Playgroud)

它找到进行替换的行号.可以使用Tim的代码捕获它,以避免多次将数据加载到内存中.

Tim*_*ter 5

如果您有非常大的文件,您应该尝试 MemoryMappedFile为此目的而设计的文件(文件> 1GB),并允许将文件的"窗口"读入内存.但它并不容易使用.

一个简单的优化是逐行读取和替换

int lineNumber = 0;
var _replacementInfo = new Dictionary<int, List<string>>();

using (StreamReader sr = new StreamReader(logF))
{
    using (StreamWriter sw = new StreamWriter(logF_Temp))
    {
        while (!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            lineNumber++;
            foreach (var kv in replacements)
            {
                bool contains = line.Contains(kv.Key);
                if (contains)
                {
                    List<string> lineReplaceList;
                    if (!_replacementInfo.TryGetValue(lineNumber, out lineReplaceList))
                        lineReplaceList = new List<string>();
                    lineReplaceList.Add(kv.Key);
                    _replacementInfo[lineNumber] = lineReplaceList;

                    line = line.Replace(kv.Key, kv.Value);
                }
            }
            sw.WriteLine(line);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,File.Copy(logF_Temp, logF, true);如果你想覆盖旧的,你可以使用.