Как сохранить вывод консольного приложения

Мне нужен совет о том, как заставить консольное приложение С# отображать текст пользователю через стандартный вывод, в то же время доступ к нему позже. Фактической функцией, которую я хотел бы реализовать, является сброс всего выходного буфера в текстовый файл в конце выполнения программы.

Обходной путь, который я использую, в то время как я не нахожу более чистого подхода, - это подкласс TextWriter, переопределяющий методы записи, чтобы они как записывали в файл, так и вызывали оригинальную запись stdout. Что-то вроде этого:

public class DirtyWorkaround {
  private class DirtyWriter : TextWriter {
    private TextWriter stdoutWriter;
    private StreamWriter fileWriter;

    public DirtyWriter(string path, TextWriter stdoutWriter) {
      this.stdoutWriter = stdoutWriter;
      this.fileWriter = new StreamWriter(path);
    }

    override public void Write(string s) {
      stdoutWriter.Write(s);

      fileWriter.Write(s);
      fileWriter.Flush();
    }

    // Same as above for WriteLine() and WriteLine(string),
    // plus whatever methods I need to override to inherit
    // from TextWriter (Encoding.Get I guess).
  }

  public static void Main(string[] args) {
    using (DirtyWriter dw = new DirtyWriter("path", Console.Out)) {
      Console.SetOut(dw);

      // Teh codez
    }
  }
}

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

Кроме того, извините неточности с вышеуказанным кодом (пришлось написать его ad hoc, извините;).

Ответ 1

Идеальное решение для этого - использовать log4net с консольным приложением и файловым приложением. Есть много других доступных приложений. Он также позволяет отключать и отключать различные приложения во время выполнения.

Ответ 2

Я не думаю, что с вашим подходом что-то не так.

Если вы хотите использовать повторно используемый код, подумайте о внедрении класса с именем MultiWriter или somesuch, который принимает в качестве входных данных два (или N?) TextWriter потоков и распространяет все записи, флеши и т.д. на эти потоки. Затем вы можете сделать это с файлом/консолью, но так же легко можете разделить любой выходной поток. Полезно!

Ответ 3

Наверное, не то, что вы хотите, но на всякий случай... По-видимому, PowerShell реализует версию почтенной команды tee. Это в значительной степени предназначено именно для этой цели. Так что... курите, если вы их получите.

Ответ 4

Я бы сказал, что имитирует диагностику, которую использует сама .NET(Trace и Debug).

Создайте класс "output", который может иметь разные классы, которые придерживаются интерфейса вывода текста. Вы отправляете отчет по выходному классу, он автоматически отправляет результат, указанный для добавленных вами классов (ConsoleOutput, TextFileOutput, WhateverOutput).. И так далее.. Это также оставляет вас открытым, чтобы добавить другие "выходные" типы (такие как xml/xslt, чтобы получить хорошо отформатированный отчет?).

Посмотрите Trace Listeners Collection, чтобы увидеть, что я имею в виду.

Ответ 5

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

Для конкретной проблемы, которую вы пытаетесь решить здесь, для части пользовательского взаимодействия становится проще изменять свое поведение с Console.WriteLine на файл ввода/вывода.

Ответ 6

Я работаю над реализацией подобной функции для захвата вывода, отправленного на консоль, и сохранения его в журнале, все еще передавая результат в реальном времени в обычную консоль, чтобы он не нарушал приложение (например, если он консольное приложение!).

Если вы все еще пытаетесь сделать это в своем собственном коде, сохраняя вывод консоли (в отличие от использования системы ведения журнала, чтобы сохранить только ту информацию, которая вам действительно нужна), я думаю, вы можете избежать флеша после каждой записи, пока вы также переопределяете Flush() и убедитесь, что он сбрасывает исходный stdoutWriter, который вы сохранили, а также ваш fileWriter. Вы хотите сделать это, если приложение пытается очистить частичную строку до консоли для немедленного отображения (например, приглашение ввода, индикатор прогресса и т.д.), Чтобы переопределить обычную буферизацию строк.

Если этот подход имеет проблемы с буферизацией слишком длинным, вам может потребоваться убедиться, что WriteLine() очищает stdoutWriter (но, вероятно, не нужно очищать fileWriter, за исключением случаев, когда ваш флеш() отменяет называется). Но я бы подумал, что исходный Console.Out (фактически идущий на консоль) автоматически сбросит его буфер по новой строке, поэтому вам не придется его принудительно.

Вы также можете переопределить Close() для (заподлицо и) закрыть fileWriter (и, вероятно, stdoutWriter), но я не уверен, действительно ли это необходимо или если Close() в base TextWriter выдает Flush() (который вы уже переопределили), и вы можете положиться на выход приложения, чтобы закрыть файл. Вы, наверное, должны проверить, что он покраснел на выходе, если быть уверенным. И имейте в виду, что аномальный выход (сбой), вероятно, не приведет к буферизации вывода. Если это проблема, может понадобиться промывка fileWriter в новой строке, но это может быть еще одна сложная червь червей.