2010-08-04 72 views
33

Tôi đang tạo phương thức trong C# tạo tệp văn bản cho số Google Product Feed. Nguồn cấp dữ liệu sẽ chứa tối đa 30.000 bản ghi và tệp văn bản hiện có trọng số là ~ 7Mb.Làm thế nào để ghi một tệp văn bản lớn vào C# một cách hiệu quả?

Đây là mã tôi hiện đang sử dụng (một số dòng được xóa vì mục đích ngắn gọn).

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()); 
    } 
} 

Tôi tự hỏi nếu StringBuilder là công cụ thích hợp cho công việc. Sẽ có hiệu suất đạt được nếu tôi sử dụng một TextWriter thay thế?

Tôi không biết nhiều về hiệu năng IO nên bất kỳ trợ giúp hoặc cải tiến chung nào đều sẽ được đánh giá cao. Cảm ơn.

+0

Kể từ thời điểm tôi viết câu hỏi này, dự án Linq2Csv ra đời. Đó là cách tốt hơn để xử lý mã tôi đang viết. http://nuget.org/packages/LinqToCsv – jessegavin

+0

bất kỳ mã nguồn đầy đủ nào có giải pháp? – Kiquenet

+0

Xin lỗi, nó được viết cho một trong những khách hàng của tôi. Bạn thực sự nên nhìn vào Linq2Csv. Nó sẽ làm cho việc này trở nên dễ dàng hơn nhiều. – jessegavin

Trả lời

61

Hoạt động của tệp I/O thường được tối ưu hóa tốt trong các hệ điều hành hiện đại. Bạn không nên cố gắng lắp ráp toàn bộ chuỗi cho tệp trong bộ nhớ ... chỉ cần viết từng mảnh một. Các FileStream sẽ chăm sóc đệm và cân nhắc hiệu suất khác.

Bạn có thể thực hiện thay đổi này một cách dễ dàng bằng cách di chuyển:

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

để phía trên cùng của chức năng, và loại bỏ các StringBuilder viết trực tiếp vào tập tin để thay thế.

Có nhiều lý do tại sao bạn nên tránh xây dựng chuỗi lớn trong bộ nhớ:

  1. Nó thực sự có thể thực hiện tồi tệ hơn, vì StringBuilder có để tăng công suất của nó như là bạn viết thư cho nó, dẫn đến việc tái phân bổ và sao chép bộ nhớ.
  2. Nó có thể đòi hỏi nhiều bộ nhớ hơn bạn có thể phân bổ vật lý - điều này có thể dẫn đến việc sử dụng bộ nhớ ảo (tệp hoán đổi) chậm hơn nhiều so với RAM.
  3. Đối với các tệp thực sự lớn (> 2Gb), bạn sẽ hết dung lượng địa chỉ (trên nền tảng 32 bit) và sẽ không bao giờ hoàn thành.
  4. Để viết nội dung StringBuilder vào một tệp bạn phải sử dụng ToString() có hiệu quả tăng gấp đôi mức tiêu thụ bộ nhớ của quá trình vì cả hai bản sao phải nằm trong bộ nhớ trong một khoảng thời gian. Thao tác này cũng có thể thất bại nếu không gian địa chỉ của bạn bị phân mảnh đầy đủ, sao cho không thể cấp phát một khối bộ nhớ liền nhau.
+0

Câu trả lời hay. Điều chỉnh có thể được thử bằng cách sử dụng quá tải constructor StreamWriter cho phép bạn xác định bufferSize ... –

+0

Xin cảm ơn câu trả lời của bạn! Tôi đánh giá cao việc bạn dành thời gian để thêm một số giải thích thêm về cách xử lý loại kịch bản này. – jessegavin

+0

5 năm sau ... là lớp 'FileStream' vẫn là phương pháp tốt nhất để viết các tập tin văn bản ~ 7MB? – n00dles

10

Viết một chuỗi tại một thời điểm bằng cách sử dụng StreamWriter.Write thay vì lưu vào bộ nhớ cache mọi thứ trong một StringBuilder.

+4

Tôi thực sự hy vọng bạn không có nghĩa là cho anh ta để viết một * bit * tại một thời điểm. –

+0

@JSBangs - lol - đã sửa đổi. –

+0

Mặc dù đây là một câu trả lời hay. Tôi có một tập tin đó là khoảng 20Mb trong kích thước và vấn đề tôi đang phải đối mặt là StreamWriter thực sự đặt của một vận chuyển trở lại/dòng mới ở cuối. Tôi đang cố gắng để loại bỏ trở lại vận chuyển thêm vào cuối cùng và như nó đã được chỉ ra StringBuilder không phải là một giải pháp tuyệt vời cho hiệu suất hoặc kích thước. Tôi đã thử StreamReader.Peek() để xem trước dòng này trước khi kết thúc. Bất kỳ ý tưởng nào? – petersmm

24

Chỉ cần di chuyển câu lệnh using để nó bao gồm toàn bộ mã của bạn và viết trực tiếp vào tệp. Tôi thấy không có điểm trong việc giữ nó tất cả trong bộ nhớ đầu tiên.

Các vấn đề liên quan