大字符串:System.OutOfMemoryException

时间:2011-03-07 12:17:24

标签: c# .net mono out-of-memory

var fileList = Directory.GetFiles("./", "split*.dat");
int fileCount = fileList.Length;
int i = 0;
foreach (string path in fileList)
{
    string[] contents = File.ReadAllLines(path); // OutOfMemoryException
    Array.Sort(contents);
    string newpath = path.Replace("split", "sorted");
    File.WriteAllLines(newpath, contents);
    File.Delete(path);
    contents = null;
    GC.Collect();

    SortChunksProgressChanged(this, (double)i / fileCount);
    i++;
}

对于包含~20-30个大行(每行~20mb)的文件,当我执行ReadAllLines方法时,我有OutOfMemoryException。为什么这个异常会引发?我该如何解决? 附:我在MacOS上使用Mono

3 个答案:

答案 0 :(得分:7)

对于执行具有潜在无限结果的操作,您应始终非常小心。在你的情况下阅读文件。如您所述,文件大小和/或行长度是无限的。

答案在于读取“足够”的一行来排序然后跳过字符直到下一行并阅读下一行“足够”。您可能希望创建一个行索引查找,以便当您达到不明确的行排序顺序时,您可以返回以从该行获取更多数据(搜索到文件位置)。当你回去时,你只需要阅读下一个可排序的块来消除冲突线的歧义。

您可能需要考虑文件编码,不要直接使用字节,除非您知道每个字符只有一个字节。

内置排序并不像你想的那么快。

旁注:

  1. 如果你打电话给GC。*你可能做错了
  2. 设置内容= null对您没有帮助
  3. 如果你正在使用foreach并维护索引,那么你可能会更好地使用for(int i ...)来提高可读性

答案 1 :(得分:2)

好的,让我给你一个提示,帮助你完成家庭作业。将完整文件加载到内存中 - 如您所知 - 不起作用,因为它是作为赋值的前提条件。您需要找到一种方法来随意延迟从磁盘加载数据并尽快丢弃尽可能多的数据。因为单行可能太大,所以你必须一次做一个char。

尝试创建一个表示一行抽象的类,例如通过包装该行的起始索引和结束索引。当你让这个类实现IComparable<T>时,它允许你用其他行对该行进行排序。同样,诀窍是能够一次一个地从文件中读取字符。您需要直接使用Stream s(File.Open)。

执行此操作时,您将能够编写以下应用程序代码:

List<FileLine> lines = GetLines("fileToSort.dat");

lines.Sort();

foreach (var line in lines)
{
    line.AppendToFile("sortedFile.dat");
}

您的任务是实施GetLines(string path)并创建FileLine课程。

请注意,我假设实际的行数足够小,List<FileLine>将适合内存(这意味着大约最多40,000,000行)。如果行数可以更高,你甚至需要一种更灵活的方法,但由于你说的是20到30行,这应该不是问题。

答案 2 :(得分:-1)

基本上你的暴力是公牛。你违反了你给你的家庭作业的约束,这个约束已经被放在那里让你思考更多。

正如你所说:

  

我必须实现外部排序并向我的老师展示它适用于比我大的文件   RAM

好的,所以你认为你将如何读取文件;)这是故意的。 ReadAllLiens不实现增量外部排序。结果,它吹了。