将大文本文件加载到字符串中

时间:2015-02-05 17:39:23

标签: c# string file-handling

我希望将150 MB的文本文件加载到字符串中。该文件是UTF16编码的,因此它将生成一个内存大约150 MB的字符串。我尝试过的所有方法都会导致Out of Memory异常。

我知道这是一个巨大的字符串,当然不是我喜欢做的事情。但是,目前我无能为力,而且没有对即将出门的应用程序进行太多深刻的改动。该文件中没有均匀分布的行集。一行可以包含整个文件大小的80%左右。

以下是我尝试的内容:

方法1

// Both of these throw Out of Memory exception
var s = File.ReadAllText(path)
var s = File.ReadAllText(path, Encoding.Unicode);

方法2

var sb = new StringBuilder();

// I've also tried a few other iterations on this with other types of streams
using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
  string line;
  while ((line = sr.ReadLine()) != null)
  {
    sb.AppendLine(line);
  }
}

// This throws an exception
sb.ToString();

方法3

using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader sr = new StreamReader(fs, Encoding.Unicode))
{
  int initialSize = (int)fs.Length / 2;  // Comes to a value of 73285158 with my test file
  var sb = new StringBuilder(initialSize); // This throws an exception

  string line;
  while ((line = sr.ReadLine()) != null)
  {
    sb.AppendLine(line);
  }

  sb.ToString();
}

那么,我该怎么做才能将这个文件加载到字符串变量中?

修改:添加了根据评论解决问题的其他尝试。

2 个答案:

答案 0 :(得分:5)

到目前为止,您的两次尝试都将文件视为UTF-8。在最好的情况下,这将占用两倍的内存 - 而且很可能是无效数据(如UTF-8),基本上。您应该尝试指定编码:

var text = File.ReadAllText(path, Encoding.Unicode);

如果这不起作用,您可以尝试使用第二个代码的变体,但是将编码指定为StreamReader(并且可能忽略BufferedStream - 我认为它不会有帮助你在这里),并指定StringBuilder的初始容量,等于文件大小的一半。

编辑:如果此行抛出异常:

var sb = new StringBuilder(initialSize);

...那你没有机会。您无法分配足够的连续内存。

可能发现您可以使用List<string>代替:

var lines = File.ReadLines(path).ToList();

...至少你有很多对象。这将需要更多的内存,但它不需要那么多的连续内存。这假设你一次需要整个文件在内存中。如果您可以改为传输数据,那将是一个更好的选择。

在一个小型控制台应用程序中,我能够使用File.ReadAllText读取相同大小的文件,同时使用32位和64位CLR ...所以它可能是一个问题您的身体记忆以及您在该计划中正在做的其他事情。

答案 1 :(得分:0)

我还试图找到一种在加载时以最小的内存使用量将文件加载到字符串中的方法。 但是我看到的所有方法都有StringBuilder的含义:首先,它收集StringBuilder中的所有内容,然后调用StringBuilder.ToString(),因此在此过程中,我们将所有相同的字符重复两次。

这意味着,对于UTF16文件-峰值内存使用率=(文件大小)* 2字节,对于UTF8文件(假设所有文本为ASCII),峰值内存使用率=(文件大小)* 4字节。最后,我们将拥有(字符串长度)* 2个字节的内存。

  • StreamReader.ReadToEnd()-使用StringBuilder
  • File.ReadAllText()-使用StreamReader.ReadToEnd()-使用StringBuilder