Как эффективно написать большой текстовый файл в C #? - PullRequest
35 голосов
/ 04 августа 2010

Я создаю метод в C #, который генерирует текстовый файл для Google Product Feed . Фид будет содержать более 30000 записей, а текстовый файл в настоящее время весит ~ 7 МБ.

Вот код, который я сейчас использую (для краткости некоторые строки удалены).

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?

Я не знаю много о производительности ввода-вывода, поэтому любая помощь или общие улучшения будут оценены. Благодаря.

Ответы [ 3 ]

67 голосов
/ 04 августа 2010

Операции файлового ввода-вывода, как правило, хорошо оптимизированы в современных операционных системах. Не следует пытаться собрать всю строку для файла в памяти ... просто записывайте ее по частям. FileStream позаботится о буферизации и других соображениях производительности.

Вы можете легко сделать это изменение, сдвинув:

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

в начало функции и избавление от записи StringBuilder непосредственно в файл.

Существует несколько причин, по которым следует избегать создания больших строк в памяти:

  1. На самом деле он может работать хуже, потому что StringBuilder должен увеличивать свою емкость при записи в него, что приводит к перераспределению и копированию памяти.
  2. Для этого может потребоваться больше памяти, чем вы можете выделить физически, что может привести к использованию виртуальной памяти (файла подкачки), которая намного медленнее, чем ОЗУ.
  3. Для действительно больших файлов (> 2 ГБ) вам не хватит адресного пространства (на 32-битных платформах), и вы никогда не сможете его завершить.
  4. Для записи содержимого StringBuilder в файл необходимо использовать ToString(), что фактически удваивает потребление памяти процессом, поскольку обе копии должны находиться в памяти в течение определенного периода времени. Эта операция также может завершиться ошибкой, если ваше адресное пространство достаточно фрагментировано, так что нельзя выделить один непрерывный блок памяти.
26 голосов
/ 04 августа 2010

Просто переместите оператор using, чтобы он охватил весь ваш код, и запишите непосредственно в файл. Сначала я не вижу смысла хранить все это в памяти.

11 голосов
/ 04 августа 2010

Пишите по одной строке за раз, используя StreamWriter.Write вместо кэширования всего в StringBuilder.

...