Phản chiếu đầu ra của bảng điều khiển thành một tệp


88

Trong ứng dụng bảng điều khiển C #, có cách nào thông minh để sao chép đầu ra bảng điều khiển thành tệp văn bản không?

Hiện tại tôi chỉ chuyển cùng một chuỗi cho cả hai Console.WriteLineInstanceOfStreamWriter.WriteLinetrong một phương thức nhật ký.

Câu trả lời:


117

Đây có thể là một số loại công việc nhiều hơn, nhưng tôi sẽ đi theo hướng khác.

Khởi tạo một TraceListenercho bảng điều khiển và một cho tệp nhật ký; sau đó sử dụng các Trace.Writecâu lệnh trong mã của bạn thay vì Console.Write. Sau đó, việc xóa nhật ký, hoặc đầu ra bảng điều khiển hoặc đính kèm cơ chế ghi nhật ký khác trở nên dễ dàng hơn.

static void Main(string[] args)
{
    Trace.Listeners.Clear();

    TextWriterTraceListener twtl = new TextWriterTraceListener(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName));
    twtl.Name = "TextLogger";
    twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;

    ConsoleTraceListener ctl = new ConsoleTraceListener(false);
    ctl.TraceOutputOptions = TraceOptions.DateTime;

    Trace.Listeners.Add(twtl);
    Trace.Listeners.Add(ctl);
    Trace.AutoFlush = true;

    Trace.WriteLine("The first line to be in the logfile and on the console.");
}

Theo như tôi có thể nhớ lại, bạn có thể xác định trình lắng nghe trong cấu hình ứng dụng để có thể kích hoạt hoặc hủy kích hoạt ghi nhật ký mà không cần chạm vào bản dựng.


4
Thật hoàn hảo - cảm ơn. Tôi đã biết về Log4Net nhưng có vẻ sai khi phải kéo vào một thư viện cho một cái gì đó như thế này.
xyz

3
Tôi không biết tại sao họ không tạo ra một thỏa thuận lớn hơn với Trace - theo tôi thì có vẻ như nó sẽ hoạt động tốt cho việc ghi nhật ký quy mô sản xuất, nhưng mọi người đều muốn sử dụng thêm một thư viện (như log4net) để làm điều đó.
Người lập mã

Đây là phản chiếu một chiều. Ý tôi là nếu bạn có một bảng điều khiển tương tác và nhận một số dữ liệu từ người dùng và muốn ghi lại mọi thứ trong một tệp, giải pháp này không hoạt động. Mặc dù thực tế đơn giản này câu hỏi của tôi đã được đóng lại. Tại đây: stackoverflow.com/questions/3886895/…
Xaqron

6
Tôi thực sự thích giải pháp này, vì vậy tôi đã tạo một blog nhanh về nó với một số thao tác dọn dẹp nhỏ và hướng dẫn một số lỗi trong quá trình thực hiện. mcrook.com/2014/11/quick-and-easy-console-logging-trace.html Cảm ơn vì giải pháp tuyệt vời :)
Michael Crook

51

Đây là một lớp đơn giản phân lớp TextWriter để cho phép chuyển hướng đầu vào tới cả tệp và bảng điều khiển.

Sử dụng nó như thế này

  using (var cc = new ConsoleCopy("mylogfile.txt"))
  {
    Console.WriteLine("testing 1-2-3");
    Console.WriteLine("testing 4-5-6");
    Console.ReadKey();
  }

Đây là lớp học:

class ConsoleCopy : IDisposable
{

  FileStream fileStream;
  StreamWriter fileWriter;
  TextWriter doubleWriter;
  TextWriter oldOut;

  class DoubleWriter : TextWriter
  {

    TextWriter one;
    TextWriter two;

    public DoubleWriter(TextWriter one, TextWriter two)
    {
      this.one = one;
      this.two = two;
    }

    public override Encoding Encoding
    {
      get { return one.Encoding; }
    }

    public override void Flush()
    {
      one.Flush();
      two.Flush();
    }

    public override void Write(char value)
    {
      one.Write(value);
      two.Write(value);
    }

  }

  public ConsoleCopy(string path)
  {
    oldOut = Console.Out;

    try
    {
      fileStream = File.Create(path);

      fileWriter = new StreamWriter(fileStream);
      fileWriter.AutoFlush = true;

      doubleWriter = new DoubleWriter(fileWriter, oldOut);
    }
    catch (Exception e)
    {
      Console.WriteLine("Cannot open file for writing");
      Console.WriteLine(e.Message);
      return;
    }
    Console.SetOut(doubleWriter);
  }

  public void Dispose()
  {
    Console.SetOut(oldOut);
    if (fileWriter != null)
    {
      fileWriter.Flush();
      fileWriter.Close();
      fileWriter = null;
    }
    if (fileStream != null)
    {
      fileStream.Close();
      fileStream = null;
    }
  }

}

5
Tôi nghĩ đây là giải pháp hoàn chỉnh nhất. Không cần phải ghi đè tất cả các quá tải của các phương thức Write / WriteLine và trong suốt đối với mã khác. Vì vậy, tất cả hoạt động của Console sẽ được sao chép vào tệp mà không thực hiện bất kỳ thay đổi nào đối với mã khác.
papadi

3
Cảm ơn anh bạn! Thật tuyệt vời! Tôi vừa thay thế File.Create bằng File.Open (đường dẫn, FileMode.Append, FileAccess.Write, FileShare.Read); vì tôi không muốn xóa nhật ký cũ khi khởi động và tôi muốn có thể mở tệp nhật ký trong khi chương trình vẫn đang chạy.
John

1
Điều này không yêu cầu tôi phải thay thế tất cả các cuộc gọi hiện có của tôi Console.WriteLine(), đó chính xác là những gì tôi muốn.
x6herbius

Đối với bất cứ ai đu đưa bởi điều đó, bối rối làm thế nào điều này làm điều này. Tìm kiếm Console.SetOut(doubleWriter);. Đó là sửa đổi toàn cục cho Console, Tôi hơi mất thời gian vì tôi đã quá quen với việc làm việc trong các ứng dụng mà thực tế không có gì là toàn cầu. Đồ tốt!
Douglas Gaskell

13

Kiểm tra log4net . Với log4net, bạn có thể thiết lập bảng điều khiển và trình phụ lục tệp có thể xuất thông báo nhật ký cho cả hai nơi chỉ với một câu lệnh nhật ký duy nhất.


6
Chà, tôi nghĩ rằng nên tránh việc nói dối thêm nếu nó có thể được thực hiện với những gì đã có.
Oliver Friedrich

Tôi cũng khuyên bạn nên đăng nhập log4net, nhưng có vẻ như NLog đang chiếm vị trí trong cộng đồng.
Mark Richman

10

Bạn không thể chỉ chuyển hướng đầu ra đến một tệp bằng cách sử dụng >lệnh?

c:\>Console.exe > c:/temp/output.txt

Nếu bạn cần nhân bản, bạn có thể thử tìm phiên bản win32 của phiên bản này teesẽ chia đầu ra thành một tệp.

Xem /superuser/74127/tee-for-windows để chạy tee từ PowerShell


8
Tôi cần phải soi gương. Đó là lý do tại sao nó được đề cập trong chủ đề và thân bài. Cảm ơn vì mẹo mặc dù :)
xyz

8

Bạn có thể phân lớp lớp TextWriter, sau đó gán thể hiện của nó cho Console.Out bằng phương thức Console.SetOut - phương thức này đặc biệt thực hiện tương tự như truyền cùng một chuỗi cho cả hai phương thức trong phương thức nhật ký.

Một cách khác có thể khai báo lớp Console của riêng bạn và sử dụng câu lệnh using để phân biệt giữa các lớp:

using Console = My.Very.Own.Little.Console;

Để truy cập bảng điều khiển tiêu chuẩn, bạn cần:

global::Console.Whatever

8

CHỈNH SỬA: Phương pháp này cung cấp khả năng chuyển hướng thông tin bảng điều khiển đến từ gói của bên thứ ba. ghi đè phương thức WriteLine là tốt cho tình huống của tôi, nhưng bạn có thể cần ghi đè các phương thức Viết khác tùy thuộc vào gói của bên thứ ba.

Đầu tiên, chúng ta cần tạo một lớp mới vốn có từ StreamWriter, ví dụ: CombineWriter;

Sau đó, init một bản tin tức thời mới của CombineWriter với Console.Out;

Cuối cùng, chúng ta có thể chuyển hướng đầu ra của bảng điều khiển đến ngay lập tức lớp mới bằng Console.SetOut;

Mã sau là lớp mới hoạt động đối với tôi.

public class CombinedWriter : StreamWriter
{
    TextWriter console;
    public CombinedWriter(string path, bool append, Encoding encoding, int bufferSize, TextWriter console)
        :base(path, append, encoding, bufferSize)
    {
        this.console = console;
        base.AutoFlush = true; // thanks for @konoplinovich reminding
    }
    public override void WriteLine(string value)
    {
        console.Write(value);
        base.WriteLine(value);
    }
}

Bằng cách này, chúng tôi sẽ không bỏ lỡ bất kỳ thứ gì được hiển thị trong bảng điều khiển.
Tiếp tục suy nghĩ vào

1
Bạn nên ghi đè phương pháp sau đây public override void Write(char value);, public override void Write(char[] buffer);, public override void Write(string value);public override void Write(char[] buffer, int index, int count);. Nếu không, nó sẽ không in ra bảng điều khiển nếu bạn sử dụng WriteLine(format, ...)phương pháp này.
Dmytro Ovdiienko

6

Log4net có thể làm điều này cho bạn. Bạn sẽ chỉ viết một cái gì đó như thế này:

logger.info("Message");

Một cấu hình sẽ xác định xem bản in sẽ chuyển đến bảng điều khiển, tệp hay cả hai.


4

Tôi nghĩ những gì bạn đang sử dụng là cách tiếp cận tốt nhất. Một phương pháp đơn giản để phản ánh đầu ra của bạn về cơ bản.

Đầu tiên khai báo một TextWriter toàn cục ở đầu:

private TextWriter txtMirror = new StreamWriter("mirror.txt");

Sau đó, tạo một phương pháp để viết:

// Write empty line
private void Log()
{
    Console.WriteLine();
    txtMirror.WriteLine();
}

// Write text
private void Log(string strText)
{
    Console.WriteLine(strText);
    txtMirror.WriteLine(strText);
}

Bây giờ, thay vì sử dụng Console.WriteLine("...");, hãy sử dụng Log("...");. Đơn giản như thế. Nó thậm chí còn ngắn hơn!


Có thể có một số rắc rối nếu bạn thay đổi vị trí ( Console.SetCursorPosition(x, y);), nhưng nếu không thì hoạt động tốt, tôi cũng sử dụng nó!

BIÊN TẬP

Tất nhiên, bạn có thể tạo một phương thức cho Console.Write();cùng một cách nếu bạn không chỉ sử dụng WriteLines


1
Đây là giải pháp đơn giản nhất. Đừng quên thêm phần này vào cuối chương trình của bạn: <br/> txtMirror.Flush (); txtMirror.Close ();
Dominic Isaia

3

Theo gợi ý của Arul, sử dụng Console.SetOutcó thể được sử dụng để chuyển hướng đầu ra đến tệp văn bản:

Console.SetOut(new StreamWriter("Output.txt"));

2

Quyết định sử dụng một lớp, được kế thừa từ StreamWriter, các đề xuất của người dùng Keep Thinking, hoạt động. Nhưng tôi phải thêm vào cơ sở hàm tạo.AutoFlush = true:

{
    this.console = console;
    base.AutoFlush = true;
}

và một cuộc gọi rõ ràng đến trình hủy:

public new void Dispose ()
{
    base.Dispose ();
}

Nếu không, tệp sẽ bị đóng sớm hơn khi anh ấy ghi lại tất cả dữ liệu.

Tôi đang sử dụng nó như:

CombinedWriter cw = new CombinedWriter ( "out.txt", true, Encoding.Unicode, 512, Console.Out );
Console.SetOut (cw);

2

Cảm ơn Keep Thinking vì giải pháp tuyệt vời! Tôi đã thêm một số ghi đè khác để tránh ghi lại một số sự kiện ghi bảng điều khiển nhất định (cho mục đích của tôi) chỉ được mong đợi cho hiển thị bảng điều khiển.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RedirectOutput
{
    public class CombinedWriter  : StreamWriter
    {
        TextWriter console;
        public CombinedWriter(string path, bool append, TextWriter consoleout)
            : base(path, append)
        {
            this.console = consoleout;
            base.AutoFlush = true;
        }
        public override void Write(string value)
        {
            console.Write(value);
            //base.Write(value);//do not log writes without line ends as these are only for console display
        }
        public override void WriteLine()
        {
            console.WriteLine();
            //base.WriteLine();//do not log empty writes as these are only for advancing console display
        }
        public override void WriteLine(string value)
        {
            console.WriteLine(value);
            if (value != "")
            {
                base.WriteLine(value);
            }
        }
        public new void Dispose()
        {
            base.Dispose();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            CombinedWriter cw = new CombinedWriter("combined.log", false, Console.Out);
            Console.SetOut(cw);
            Console.WriteLine("Line 1");
            Console.WriteLine();
            Console.WriteLine("Line 2");
            Console.WriteLine("");
            for (int i = 0; i < 10; i++)
            {
                Console.Write("Waiting " + i.ToString());
                Console.CursorLeft = 0;
            }
            Console.WriteLine();
            for (int i = 0; i < 10; i++)
            {
                Console.Write("Waiting " + i.ToString());
            }
            Console.WriteLine();
            Console.WriteLine("Line 3");
            cw.Dispose();
        }
    }
}

Bất kỳ lý do cụ thể nào khiến bạn thay thế phương thức Dispose và sau đó gọi base.Dispose ()?
Adam Plocher

1

Nếu bạn sao chép đầu ra bảng điều khiển từ mã mà bạn không kiểm soát, chẳng hạn như thư viện của bên thứ ba, tất cả các thành viên của TextWriter sẽ bị ghi đè. Mã sử ​​dụng ý tưởng từ chủ đề này.

Sử dụng:

using (StreamWriter writer = new StreamWriter(filePath))
{
   using (new ConsoleMirroring(writer))
   {
       // code using console output
   }
}

Lớp ConsoleMirroring

public class ConsoleMirroring : TextWriter
{
    private TextWriter _consoleOutput;
    private TextWriter _consoleError;

    private StreamWriter _streamWriter;

    public ConsoleMirroring(StreamWriter streamWriter)
    {
        this._streamWriter = streamWriter;
        _consoleOutput = Console.Out;
        _consoleError = Console.Error;

        Console.SetOut(this);
        Console.SetError(this);
    }

    public override Encoding Encoding { get { return _consoleOutput.Encoding; } }
    public override IFormatProvider FormatProvider { get { return _consoleOutput.FormatProvider; } }
    public override string NewLine { get { return _consoleOutput.NewLine; } set { _consoleOutput.NewLine = value; } }

    public override void Close()
    {
        _consoleOutput.Close();
        _streamWriter.Close();
    }

    public override void Flush()
    {
        _consoleOutput.Flush();
        _streamWriter.Flush();
    }

    public override void Write(double value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }
    public override void Write(string value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(object value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(decimal value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(float value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(bool value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(int value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(uint value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(ulong value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(long value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(char[] buffer)
    {
        _consoleOutput.Write(buffer);
        _streamWriter.Write(buffer);

    }

    public override void Write(char value)
    {
        _consoleOutput.Write(value);
        _streamWriter.Write(value);

    }

    public override void Write(string format, params object[] arg)
    {
        _consoleOutput.Write(format, arg);
        _streamWriter.Write(format, arg);

    }

    public override void Write(string format, object arg0)
    {
        _consoleOutput.Write(format, arg0);
        _streamWriter.Write(format, arg0);

    }

    public override void Write(string format, object arg0, object arg1)
    {
        _consoleOutput.Write(format, arg0, arg1);
        _streamWriter.Write(format, arg0, arg1);

    }

    public override void Write(char[] buffer, int index, int count)
    {
        _consoleOutput.Write(buffer, index, count);
        _streamWriter.Write(buffer, index, count);

    }

    public override void Write(string format, object arg0, object arg1, object arg2)
    {
        _consoleOutput.Write(format, arg0, arg1, arg2);
        _streamWriter.Write(format, arg0, arg1, arg2);

    }

    public override void WriteLine()
    {
        _consoleOutput.WriteLine();
        _streamWriter.WriteLine();

    }

    public override void WriteLine(double value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(decimal value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(string value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(object value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(float value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(bool value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(uint value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(long value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(ulong value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(int value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(char[] buffer)
    {
        _consoleOutput.WriteLine(buffer);
        _streamWriter.WriteLine(buffer);

    }
    public override void WriteLine(char value)
    {
        _consoleOutput.WriteLine(value);
        _streamWriter.WriteLine(value);

    }
    public override void WriteLine(string format, params object[] arg)
    {
        _consoleOutput.WriteLine(format, arg);
        _streamWriter.WriteLine(format, arg);

    }
    public override void WriteLine(string format, object arg0)
    {
        _consoleOutput.WriteLine(format, arg0);
        _streamWriter.WriteLine(format, arg0);

    }
    public override void WriteLine(string format, object arg0, object arg1)
    {
        _consoleOutput.WriteLine(format, arg0, arg1);
        _streamWriter.WriteLine(format, arg0, arg1);

    }
    public override void WriteLine(char[] buffer, int index, int count)
    {
        _consoleOutput.WriteLine(buffer, index, count);
        _streamWriter.WriteLine(buffer, index, count);

    }
    public override void WriteLine(string format, object arg0, object arg1, object arg2)
    {
        _consoleOutput.WriteLine(format, arg0, arg1, arg2);
        _streamWriter.WriteLine(format, arg0, arg1, arg2);

    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Console.SetOut(_consoleOutput);
            Console.SetError(_consoleError);
        }
    }
}

0

Bạn thực sự có thể tạo một bản sao trong suốt của Console.Out to Trace bằng cách triển khai lớp của riêng bạn được kế thừa từ TextWriter và ghi đè phương thức WriteLine.

Trong WriteLine, bạn có thể ghi nó vào Trace, sau đó có thể được cấu hình để ghi vào tệp.

Tôi thấy câu trả lời này rất hữu ích: https://stackoverflow.com/a/10918320/379132

Nó thực sự đã làm việc cho tôi!


0

Câu trả lời của tôi dựa trên câu trả lời không được chấp nhận được bình chọn nhiều nhất và cũng là câu trả lời ít được bình chọn nhất mà tôi nghĩ là giải pháp thanh lịch nhất cho đến nay. Nó chung chung hơn một chút về loại luồng bạn có thể sử dụng (bạn có thể sử dụng MemoryStreamví dụ), nhưng tôi đã bỏ qua tất cả các chức năng mở rộng có trong câu trả lời sau cho ngắn gọn.

class ConsoleMirrorWriter : TextWriter
{
    private readonly StreamWriter _writer;
    private readonly TextWriter _consoleOut;

    public ConsoleMirrorWriter(Stream stream)
    {
        _writer = new StreamWriter(stream);
        _consoleOut = Console.Out;
        Console.SetOut(this);
    }

    public override Encoding Encoding => _writer.Encoding;

    public override void Flush()
    {
        _writer.Flush();
        _consoleOut.Flush();
    }

    public override void Write(char value)
    {
        _writer.Write(value);
        _consoleOut.Write(value);
    }

    protected override void Dispose(bool disposing)
    {
        if (!disposing) return;
        _writer.Dispose();
        Console.SetOut(_consoleOut);
    }
}

Sử dụng:

using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
    // Code using console output.
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.