【问题标题】:Read Big TXT File, Out of Memory Exception读取大 TXT 文件,内存不足异常
【发布时间】:2012-11-05 03:19:39
【问题描述】:

我要读取大 TXT 文件,大小为 500 MB, 首先我使用

var file = new StreamReader(_filePath).ReadToEnd();  
var lines = file.Split(new[] { '\n' });

但它抛出了内存异常然后我尝试逐行读取,但在读取了大约 150 万行后它又抛出了内存异常

  using (StreamReader r = new StreamReader(_filePath))
         {            
             while ((line = r.ReadLine()) != null)            
                 _lines.Add(line);            
         }

或者我用过

  foreach (var l in File.ReadLines(_filePath))
            {
                _lines.Add(l);
            }

但我又收到了

“System.OutOfMemoryException”类型的异常发生在 mscorlib.dll 但未在用户代码中处理

我的机器是具有 8GB 内存的强大机器,所以它不应该是我的机器问题。

p.s:我尝试在 NotePadd++ 中打开此文件,但收到“文件太大无法打开”异常。

【问题讨论】:

  • 问题是什么?你只是在描述事物。
  • 将所有内容存储在集合中的意义何在?
  • 您说的是“500 行”,但就 bytescharacters 而言,文件有多大? 500 行 80 个字符应该不是问题 - 每行 500 行 10 亿个字符显然是。
  • @Behnam - 8GB 内存中有 500GB 文件?那不合适。您将需要找到其他处理文件的方法,这不需要内存中的全部内容。每行处理(或者可能是少量行)
  • 您可以通过将整个文件加载到内存中来修复它。显然,您的设计不适合应用程序的需求。您最终将如何处理这些数据?任何处理,过滤等?也许您需要先将其存储在数据库中。

标签: c# .net exception .net-4.0 out-of-memory


【解决方案1】:

只需使用File.ReadLines,它会返回一个IEnumerable<string>,并且不会一次将所有行加载到内存中。

foreach (var line in File.ReadLines(_filePath))
{
    //Don't put "line" into a list or collection.
    //Just make your processing on it.
}

【讨论】:

  • 即使只使用空循环 foreach (var line in File.ReadLines(_filePath)) { }
  • @Behnam 您确定您没有从程序的其他部分收到此错误。在一个空的解决方案中试试这个。
  • 我刚刚创建了一个控制台应用程序,它只有一行代码 foreach (var line in File.ReadLines(_filePath)) { },但它再次创建异常。
  • @Behnam 我刚刚用 8.7GB 文本文件(120,000,000 行)对其进行了测试,并且运行良好。
【解决方案2】:

异常的原因似乎是增长 _lines 集合但没有读取大文件。您正在阅读行和adding to some collection _lines which will be taking memory and causing out of memory execption。您可以应用过滤器以仅将所需的行放入 _lines 集合。

【讨论】:

  • 我只是删除了将数据添加到_lines集合的行,但问题仍然存在。
  • 现在抛出什么异常?
【解决方案3】:

我知道这是一篇旧帖子,但 Google 是在 2021 年把我发到这里的。

只是为了强调上面igrimpe的cmets:

我在 StreamReader.ReadLine() 上遇到了 OutOfMemoryException,最近循环浏览巨大文本文件的文件夹。

正如igrimpe 所提到的,您有时会遇到这种情况,您的输入文件在换行符中表现出缺乏一致性。如果您在循环文本文件时遇到这种情况,请仔细检查您的输入文件中是否存在意外字符/ascii 编码的十六进制或二进制字符串等。

在我的例子中,我将 60 GB 有问题的文件拆分为 256 MB 的块,让我的文件迭代器将有问题的文本文件作为异常陷阱的一部分存储起来,然后通过删除有问题的行来修复有问题的文本文件。

【讨论】:

    【解决方案4】:

    编辑:

    将整个文件加载到内存中会导致对象增长,如果无法为对象分配足够的连续内存,.net 将引发 OOM 异常。

    答案还是一样,你需要流式传输文件,而不是读取全部内容。这可能需要重新架构您的应用程序,但是使用IEnumerable<> 方法,您可以在应用程序的不同区域堆叠业务流程并延迟处理。


    具有 8GB RAM 的“强大”机器将无法在内存中存储 500GB 文件,因为 500 大于 8。(另外,您不会得到 8,因为操作系统会保存一些,你不能在.Net中分配所有内存,32位有2GB的限制,打开文件和存储行会保存两次数据,有对象大小开销....)

    您不能将整个内容加载到内存中进行处理,您必须通过处理流式传输文件。

    【讨论】:

    • 在我的第二种方法中,我尝试使用 StreamReader,甚至删除 _lines.Add(line);行,我收到 OutOfMemoryException。所以我不太清楚你所说的流媒体是什么意思。
    • 也许“行”终结符不是应该的?如果这些行没有被 \r AND \n 终止,内部函数可能仍会将整个文件读入内存,不是吗?
    • 我不知道为什么您在没有调用_lines.Add(line) 时收到第二个代码摘录错误,也许您在其他地方有问题?行终止符可能与问题无关 - 在任何情况下都很难获得 500MB 的连续内存,除非您运行 64 位并拥有大量内存。
    • 测试行终止符是否是问题应该很容易。使用单一方法file.readline(path) 做一个控制台应用程序。如果它仍然抛出一个前任,那么一条“线”就太长了。很可能是因为在内部使用了一个字符串生成器,它必须永久增加其内部数组(即为新数组分配空间),而没有给 GC 时间来清理。
    • @igrimpe 打个招呼
    【解决方案5】:

    你必须先数行数。 它速度较慢,但​​您最多可以读取 2,147,483,647 行。

    int intNoOfLines = 0;
    using (StreamReader oReader = new 
    StreamReader(MyFilePath))
    {
        while (oReader.ReadLine() != null) intNoOfLines++;
    }
    string[] strArrLines = new string[intNoOfLines];
    int intIndex = 0;
    using (StreamReader oReader = new 
    StreamReader(MyFilePath))
    {
        string strLine;
        while ((strLine = oReader.ReadLine()) != null)
        {
           strArrLines[intIndex++] = strLine;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-22
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      相关资源
      最近更新 更多