Подтвердить что ты не робот

Как эффективно писать большой текстовый файл в С#?

Я создаю метод в С#, который генерирует текстовый файл для Google Product Feed. Канал будет содержать более 30 000 записей, а текстовый файл в настоящий момент весит ~ 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?

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

4b9b3361

Ответ 1

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

Вы можете легко сделать это изменение, перемещая:

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

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

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

  • Это может действительно ухудшиться, потому что StringBuilder должен увеличить свою емкость при записи на него, что приведет к перераспределению и копированию памяти.
  • Это может потребовать больше памяти, чем вы можете физически выделить, что может привести к использованию виртуальной памяти (файла подкачки), которая намного медленнее, чем оперативная память.
  • Для действительно больших файлов ( > 2Gb) у вас закончится адресное пространство (на 32-разрядных платформах) и не будет завершено.
  • Чтобы записать содержимое StringBuilder в файл, вы должны использовать ToString(), что эффективно удваивает потребление памяти процессом, поскольку обе копии должны находиться в памяти в течение определенного периода времени. Эта операция может также завершиться неудачно, если ваше адресное пространство достаточно фрагментировано, так что нельзя выделить один непрерывный блок памяти.

Ответ 2

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

Ответ 3

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