我在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性能一无所知,所以任何帮助或一般改进都将受到赞赏。感谢。
答案 0 :(得分:65)
文件I / O操作通常在现代操作系统中得到很好的优化。您不应该尝试在内存中组装文件的整个字符串......只需将其逐个写出来。 FileStream
将负责缓冲和其他性能考虑。
您可以通过移动轻松进行此更改:
using (StreamWriter outfile = new StreamWriter(filePath)) {
到函数的顶部,而不是直接写StringBuilder
直接写入文件。
有几个原因可以避免在内存中构建大字符串:
StringBuilder
必须在写入时增加其容量,从而导致重新分配和复制内存。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 = "";
}
}
就我而言,它比一次一行快得多。