2010-08-04 107 views
33

我正在C#中创建一个方法,该方法为Google Product Feed生成一个文本文件。 Feed中将包含超过30,000条记录,文本文件的重量约为7Mb。如何在C#中高效地编写大型文本文件?

下面是我正在使用的代码(为简洁起见删除了一些行)。

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性能如何,所以任何帮助或一般的改进将不胜感激。谢谢。

+0

自从我写这个问题的时候,Linq2Csv项目就显现出来了。这是处理我写的代码的好方法。 http://nuget.org/packages/LinqToCsv – jessegavin 2012-04-20 13:55:10

+0

任何完整的解决方案源代码? – Kiquenet 2012-08-14 09:19:23

+0

对不起,它是为我的一个客户编写的。你应该看看Linq2Csv。它会使这种事情变得更容易。 – jessegavin 2012-08-14 14:27:47

回答

61

在现代操作系统中,文件I/O操作一般都进行了优化。你不应该试图将文件的整个字符串组装到内存中......只需将它逐条写出即可。 FileStream将负责缓冲和其他性能考虑事项。

您可以通过移动使这个变化很容易:

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

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

有几个原因,你应该避免在内存中建立起来的大字符串:

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

很好的答案。可以使用StreamWriter构造函数重载来尝试调优,该重载允许您定义bufferSize ... – 2010-08-04 15:58:27

+0

嘿,谢谢您的回答!我感谢您花时间对如何处理这种情况添加一些进一步的解释。 – jessegavin 2010-08-04 16:34:55

+0

5年后...... FileStream类仍然是编写文本文件的最佳方法〜7MB? – n00dles 2015-10-22 15:45:34

10

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

+4

我真的希望你一次不要写一个*位*。 – 2010-08-04 15:43:37

+0

@JSBangs - 大声笑 - 修正。 – 2010-08-04 15:45:54

+0

虽然这是一个很好的答案。我有一个大小约20Mb的文件,我面临的问题是StreamWriter实际上在最后放置了一个回车/新行。我试图在最后删除额外的回车,因为它已经被指出了,StringBuilder对于性能或者大小来说并不是很好的解决方案。 我试过 StreamReader.Peek() 在到达结尾之前查看该行。 有什么建议吗? – petersmm 2015-07-30 10:56:36

24

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

相关问题