【问题标题】:What is the best way to merge large files?合并大文件的最佳方法是什么?
【发布时间】:2015-10-28 12:05:50
【问题描述】:

我必须合并数千个大文件(每个约 200MB)。我想知道合并这些文件的最佳方法是什么。行将有条件地复制到合并文件中。可以使用 File.AppendAllLines 还是使用 Stream.CopyTo 吗?

使用 File.AppendAllLines

for (int i = 0; i < countryFiles.Length; i++){
   string srcFileName = countryFiles[i];
   string[] countryExtractLines = File.ReadAllLines(srcFileName);  
   File.AppendAllLines(actualMergedFileName, countryExtractLines);
}

使用 Stream.CopyTo

using (Stream destStream = File.OpenWrite(actualMergedFileName)){
  foreach (string srcFileName in countryFiles){
    using (Stream srcStream = File.OpenRead(srcFileName)){
        srcStream.CopyTo(destStream);
    }
  }
}

【问题讨论】:

  • 对一个新文件使用StreamWriter,并读取你想与StreamReader合并的所有文件并写给你的作家。
  • 我怀疑很多人会回答“试试并比较两者”。
  • 我相信你会想要一个 StreamReader 并逐行遍历文件,因为这样它不会一次将所有内容都存储在内存中。
  • 您只想附加文件吗?如果是这样,请使用Stream.CopyTo(),但使用File.Open("filename", FileMode.Append) 打开要附加到的现有文件。如果你使用File.OpenWrite(),事情就会大错特错。
  • 那么你肯定不想这样做ReadAllLines,因为这会让你将 200MB 的数据加载到内存中,正如 sab669 提到的那样

标签: c# .net io


【解决方案1】:

sab669 的回答是正确的,您想使用 StreamReader 然后循环遍历文件的每一行... 我建议单独编写每个文件,否则您将很快用完许多 200mb 文件的内存

例如:

foreach(File f in files)
{
    List<String> lines = new List<String>();
    string line;
    int cnt = 0;
    using(StreamReader reader = new StreamReader(f)) {
        while((line = reader.ReadLine()) != null) {
            // TODO : Put your conditions in here
            lines.Add(line);
            cnt++;
        }
    }
    f.Close();
    // TODO : Append your lines here using StreamWriter
}

【讨论】:

  • 通过字节缓冲算法更好地复制文件。我们这里根本不需要使用字符串
  • @gabba 根据 OP,他将“需要有条件地操作每一行”。
【解决方案2】:

您可以一个接一个地编写文件。例如:

static void MergingFiles(string outputFile, params string[] inputTxtDocs)
{
    using (Stream outputStream = File.OpenWrite(outputFile))
    {
      foreach (string inputFile in inputTxtDocs)
      {
        using (Stream inputStream = File.OpenRead(inputFile))
        {
          inputStream.CopyTo(outputStream);
        }
      }
    }
}

在我看来,上面的代码确实是高性能的,因为 Stream.CopyTo() 的算法非常简单,因此该方法非常有效。反射器将其核心渲染如下:

private void InternalCopyTo(Stream destination, int bufferSize)
{
  int num;
  byte[] buffer = new byte[bufferSize];
  while ((num = this.Read(buffer, 0, buffer.Length)) != 0)
  {
     destination.Write(buffer, 0, num);
  }
}

【讨论】:

  • 这对我来说非常有效地处理大量文件。
【解决方案3】:

假设对于要附加到另一个文件的文件中的每一行,您有一个条件必须为真(即谓词)。

您可以按如下方式高效处理:

var filteredLines = 
    File.ReadLines("MySourceFileName")
    .Where(line => line.Contains("Target")); // Put your own condition here.

File.AppendAllLines("MyDestinationFileName", filteredLines);

这种方法可以扩展到多个文件,避免将整个文件加载到内存中。

如果您不想将所有行附加到文件中,而是要替换内容,您可以这样做:

File.WriteAllLines("MyDestinationFileName", filteredLines);

而不是

File.AppendAllLines("MyDestinationFileName", filteredLines);

另请注意,如果您不使用 UTF8,这些方法的重载允许您指定编码。

最后,不要被不一致的方法命名所抛出。File.ReadLines() 不会将所有行都读入内存,但File.ReadAllLines() 会。但是,File.WriteAllLines() 不会将所有行缓冲到内存中,也不希望它们都缓冲在内存中;它使用IEnumerable&lt;string&gt; 作为输入。

【讨论】:

  • 谢谢。刚刚从 MSDN 中读到:ReadLines 和 ReadAllLines 方法的区别如下: 使用 ReadLines 时,可以在返回整个集合之前开始枚举字符串集合;当您使用 ReadAllLines 时,您必须等待返回整个字符串数组才能访问该数组。因此,当您处理非常大的文件时,ReadLines 会更有效率。
  • @LUISPEREIRA 是的,所以我建议使用这种简单的方法。另请注意我最后一段关于微软命名不一致的内容!
猜你喜欢
  • 2010-09-08
  • 2014-07-11
  • 1970-01-01
  • 1970-01-01
  • 2013-09-20
  • 1970-01-01
  • 1970-01-01
  • 2020-09-01
相关资源
最近更新 更多