如何在 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# 中高效地编写大型文本文件?的主要内容,如果未能解决你的问题,请参考以下文章