2010-08-04 72 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語句,使其包含整個代碼並直接寫入文件。我認爲沒有必要先把它全部留在記憶中。

相關問題