如何在C#中有效地编写大文本文件?

时间:2010-08-04 15:39:16

标签: c# text-files io

我在C#中创建一个方法,为Google Product Feed生成一个文本文件。该Feed将包含超过30,000条记录,文本文件目前的重量约为7Mb。

这是我目前正在使用的代码(为简洁起见,删除了一些代码)。

public static void GenerateTextFile(string filePath) {

  var sb = new StringBuilder(1000);
  sb.Append("availability").Append("\t");
  sb.Append("condition").Append("\t");
  sb.Append("description").Append("\t");
  // repetitive code hidden for brevity ...
  sb.Append(Environment.NewLine);

  var items = inventoryRepo.GetItemsForSale();

  foreach (var p in items) {
    sb.Append("in stock").Append("\t");
    sb.Append("used").Append("\t");
    sb.Append(p.Description).Append("\t");
    // repetitive code hidden for brevity ...
    sb.AppendLine();
  }

  using (StreamWriter outfile = new StreamWriter(filePath)) {
      result.Append("Writing text file to disk.").AppendLine();
      outfile.Write(sb.ToString());
  }
}

我想知道StringBuilder是否适合这项工作。如果我使用TextWriter,会有性能提升吗?

我对IO性能一无所知,所以任何帮助或一般改进都将受到赞赏。感谢。

4 个答案:

答案 0 :(得分:65)

文件I / O操作通常在现代操作系统中得到很好的优化。您不应该尝试在内存中组装文件的整个字符串......只需将其逐个写出来。 FileStream将负责缓冲和其他性能考虑。

您可以通过移动轻松进行此更改:

using (StreamWriter outfile = new StreamWriter(filePath)) {

到函数的顶部,而不是直接写StringBuilder直接写入文件。

有几个原因可以避免在内存中构建大字符串:

  1. 它实际上可能会表现更差,因为StringBuilder必须在写入时增加其容量,从而导致重新分配和复制内存。
  2. 可能需要比物理分配更多的内存 - 这可能会导致使用比RAM慢得多的虚拟内存(交换文件)。
  3. 对于真正大的文件(> 2Gb),您将耗尽地址空间(在32位平台上)并且无法完成。
  4. 要将StringBuilder内容写入您必须使用的文件ToString(),这会有效地使进程的内存消耗增加一倍,因为两个副本必须在内存中保留一段时间。如果地址空间足够碎片,则此操作也可能失败,从而无法分配单个连续的内存块。

答案 1 :(得分:25)

只需移动using语句,使其包含整个代码,并直接写入文件。我认为没有必要先把它全部留在记忆中。

答案 2 :(得分:10)

使用StreamWriter.Write一次写一个字符串,而不是在StringBuilder中缓存所有内容。

答案 3 :(得分:2)

这可能是旧的,但我有一个大约有 1700 万行的文件要写 所以我最终每 10k 行对写入进行批处理,类似于这些行

for (i6 = 1; i6 <= ball; i6++) 
{ //this is middle of 6 deep nest ..
  counter++;
  // modus to get a value at every so often 10k lines
  divtrue = counter % 10000; // remainder operator % for 10k
  //  build the string of fields with \n at the end 
  lineout = lineout + whatever 
  // the magic 10k block here
  if (divtrue.Equals(0))  
  {
     using (StreamWriter outFile = new StreamWriter(@filepath, true))
     { 
         //  write the 10k lines with .write NOT writeline..
         outFile.Write(lineout); 
     } 
     // reset the string so we dont do silly like memory overflow
     lineout = ""; 
  }
}

就我而言,它比一次一行快得多。