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

Posted

技术标签:

【中文标题】如何在 C# 中高效地编写大型文本文件?【英文标题】:How to efficiently write a large text file in C#? 【发布时间】:2011-03-25 08:20:09 【问题描述】:

我正在 C# 中创建一个方法,它为 Google Product 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 性能了解不多,因此我们将不胜感激任何帮助或一般改进。谢谢。

【问题讨论】:

自从我写了这个问题之后,Linq2Csv 项目就诞生了。这是处理我正在编写的代码的更好方法。 nuget.org/packages/LinqToCsv 任何完整的源代码和解决方案? 抱歉,这是为我的一位客户编写的。你真的应该研究一下 Linq2Csv。它会让这类事情变得容易得多。 自我上次对这个问题发表评论以来已经快 5 年了,我强烈推荐 CsvHelper。 joshclose.github.io/CsvHelper 【参考方案1】:

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

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

using (StreamWriter outfile = new StreamWriter(filePath)) 

到函数的顶部,并摆脱StringBuilder直接写入文件。

您应该避免在内存中建立大字符串有几个原因:

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

【讨论】:

不错的答案。可以尝试使用 StreamWriter 构造函数重载进行调整,它允许您定义 bufferSize... 您好,感谢您的回答!感谢您抽出宝贵时间就如何处理这种情况添加一些进一步的说明。 5 年后...FileStream 类仍然是编写文本文件的最佳方法吗~7MB?【参考方案2】:

只需移动using 语句,使其包含整个代码,然后直接写入文件。我认为首先将其全部保存在内存中是没有意义的。

【讨论】:

【参考方案3】:

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

【讨论】:

我真的希望你不要让他一次写一个bit 虽然这是一个很好的答案。我有一个大小约为 20Mb 的文件,我面临的问题是 StreamWriter 实际上在末尾放置了一个回车符/换行符。我试图在最后删除那个额外的回车,正如已经指出的那样,StringBuilder 对于性能或大小来说并不是一个很好的解决方案。我试过 StreamReader.Peek() 在它到达末尾之前偷看它。有什么想法吗? @MaximusPeters 您可能在此期间找到了自己的方式,但也许您使用的是WriteLine() 方法而不是Write()【参考方案4】:

这可能很旧,但我有一个文件要写大约 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 = ""; 
  

在我的情况下,它一次比一行快得多。

【讨论】:

以上是关于如何在 C# 中高效地编写大型文本文件?的主要内容,如果未能解决你的问题,请参考以下文章

C# 如何使用 PGP 公钥简单地加密文本文件?

如何提高java读取大文本文件的效率

写入/读取文本文件 (C#)

如何有效地读取 LARGE 文本文件中的行数

如何确定文件是c#中的二进制文件还是文本文件? [复制]

两个大文本文件的高效文件比较