【问题标题】:What's the most efficient way to read a very large file in chunks?分块读取非常大的文件的最有效方法是什么?
【发布时间】:2014-02-01 16:10:37
【问题描述】:

我想读取一个大小为数百 GB 甚至 TB 的 CSV 文件。 我有一个限制,我只能读取 32MB 的文件。 我对这个问题的解决方案有点慢,我想问你是否知道更好的解决方案:

const int MAX_BUFFER = 33554432; //32MB
byte[] buffer = new byte[MAX_BUFFER];
int bytesRead;

using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read))
using (BufferedStream bs = new BufferedStream(fs))
{
    string line;
    bool stop = false;
    while ((bytesRead = bs.Read(buffer, 0, MAX_BUFFER)) != 0) //reading only 32mb chunks at a time
    {
        var stream = new StreamReader(new MemoryStream(buffer));
        while ((line = stream.ReadLine()) != null)
        {
            //process line
        }

    }
}

编辑:我添加了一个限制,说我不能逐行读取文件。

【问题讨论】:

  • 你试过File.ReadLines的性能吗?
  • @KonradKokosa 您能否在答案中给出解释和示例?老实说,我对文件处理没有那么丰富的经验,所以我不确定 File.ReadLines 和我的处理方式有什么区别
  • 顺便说一句:根据您的代码,一行可能会分成两块。
  • @L.B 你是什么意思?
  • 没有必要跳过所有这些障碍。有一个StreamReader constructor 可让您指定缓冲区大小。另外,考虑将常量定义为const int MAX_BUFFER = 32 * 1024 * 1024; 这比幻数要清楚得多。顺便说一句,我发现最佳缓冲区大小通常约为 64 KB。更大的缓冲区只会增加不必要的开销,并且通常会使您的程序变慢。

标签: c# csv file-io


【解决方案1】:

我建议在文件上简单地使用File.ReadLines。它在下面调用StreamReader.ReadLine,但它可能比为32MB 块反复处理BufferedStream 更有效。所以它很简单:

foreach (var line in File.ReadLines(filePath))
{
    //process line 
}

此外,您的代码存在问题,因为您可以在 32MB 块之间拆分行,而上述代码不会发生这种情况。

【讨论】:

  • 有没有办法确保正在使用的内部缓冲区不会超过 32MB? (尽管可以安全地假设单行不会那么大,但仍然......)
  • @YonatanNir,通过File.ReadLines的代码我们可以看到使用了public StreamReader(string path, Encoding encoding) : this(path, encoding, true, StreamReader.DefaultBufferSize)构造函数,而DefaultBufferSize只是1024。
  • 如果我也遇到不能一次读取一行文件的限制怎么办?
  • @YonatanNir 您是否想让自己尽可能地困难?请列出您问题中的所有其他限制。
  • @AndrewMorton 这是我遇到的限制。我的问题的标题是关于阅读块,但每个人都更喜欢简单的逐行解决方案。限制本身是:“你不能读取整个文件,一次一行。读取的数据的最大大小,除了查询需要返回的实际记录,不能超过 32MB。”
猜你喜欢
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 2019-01-30
  • 2013-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多