【问题标题】:Memory Efficient file append内存高效文件追加
【发布时间】:2011-02-26 04:26:13
【问题描述】:

我有几个文件的内容需要合并到一个文件中。我有以下代码可以做到这一点......但在内存使用方面似乎相当低效......你会建议一个更好的方法吗?

Util.MoveFile 函数只是考虑跨卷移动文件

   private void Compose(string[] files)
   {
       string inFile = "";
       string outFile = "c:\final.txt";

       using (FileStream fsOut = new FileStream(outFile + ".tmp", FileMode.Create))
       {
           foreach (string inFile in files)
           {
               if (!File.Exists(inFile))
               {
                   continue;
               }

               byte[] bytes;
               using (FileStream fsIn = new FileStream(inFile, FileMode.Open))
               {
                   bytes = new byte[fsIn.Length];
                   fsIn.Read(bytes, 0, bytes.Length);
               }

               //using (StreamReader sr = new StreamReader(inFile))
               //{
               //    text = sr.ReadToEnd();
               //}

               // write the segment to final file
               fsOut.Write(bytes, 0, bytes.Length);

               File.Delete(inFile);
           }
       }

       Util.MoveFile(outFile + ".tmp", outFile);

}

【问题讨论】:

  • 也许可以看到:在 C# 中连接三个文件的最快方法是什么? stackoverflow.com/questions/444309/…
  • 为什么觉得效率低?
  • 如果这些是文本文件,为什么不使用“cat”? en.wikipedia.org/wiki/Cat_%28Unix%29
  • @Alan - 代码在 Windows 上运行,所以没有“猫”。 @Changeling - 我觉得它效率低下,因为它使用了大量内存 @Mitch Wheat - 我正在查看代码,它只创建一个缓冲区,而不是大量新的字节调用......这应该真的有帮助:)
  • 你能不能使用一个while循环,每次只读取每个文件的1024字节?读取整个文件有点多。

标签: c# file merge memory-efficient


【解决方案1】:

您可以像这样使用较小的固定大小缓冲区:

byte[] bytes = new byte[8192]; // adjust this as needed
int bytesRead;
do {
    bytesRead = fsIn.Read(bytes, 0, bytes.Length);
    fsOut.Write(bytes, 0, bytesRead);
} while (bytesRead > 0);

这很容易解释,除了最后一个块,所以基本上发生的事情是我将一个 8K 字节数组传递给 Read 方法,该方法返回它实际读取的字节数。所以在 Write 调用中,我传递了介于 0 和 8192 之间的值。换句话说,在最后一个块上,即使我传递了一个 8192 字节的字节数组,bytesRead 在这种情况下可能只有 10只需要写入前 10 个字节。

编辑

我编辑了我的答案,以稍微不同的方式做到这一点。我没有使用输入文件的位置来确定何时跳出循环,而是检查 bytesRead 是否大于零。此方法适用于任何类型的流到流复制,包括没有固定或已知长度的流。

【讨论】:

  • 感谢 Josh ...我将实施这一点并分析结果
  • 请注意,就像任何事情一样,性能和内存使用量之间总是存在权衡。最快的方法是你最初展示它的方式......使用尽可能多的内存。另请注意,除非您使用特定的构造函数,否则 .NET 完成的默认缓冲仍将发生。并且文件系统仍将应用自己的写缓冲。但至少这可以让你处理大文件而不用担心 OutOfMemoryException。
  • 我会使用更大的缓冲区,例如 1Mb 用于典型磁盘。计算缓冲区大小的封套方法的背面,您需要有效地将磁盘用于非 SSD 磁盘:将磁盘寻道时间乘以吞吐量。对于现代磁盘,您得到 (~10ms)*(~100Mb/sec) = 1MB。 8KB 可能会明显减慢您的速度。
  • 完全同意 Michael 的观点,即一次分块 8K 可能会导致瓶颈。我应该澄清一下,这只是一个示例数字,但最佳缓冲区大小将取决于诸如您一次要执行多少次(是 Web 服务器吗?)和文件的平均大小等因素。
【解决方案2】:

有时调用 shell 函数比重新实现功能更好。正如 Alan 所说,您可以在 unix 系统或 Windows 上使用 CAT,您可以使用内置的命令处理器

copy file1+file2+file3 concated_file

【讨论】:

  • 感谢 preet sangha ...您也是对的 ...创建新的 shell 进程/线程有开销,但最终它应该比我能做的任何事情都更有效率..我将尝试实施和分析
猜你喜欢
  • 2011-07-08
  • 2020-07-02
  • 2011-04-05
  • 1970-01-01
  • 2020-07-19
  • 2022-07-26
  • 2019-07-09
  • 1970-01-01
  • 2019-05-29
相关资源
最近更新 更多