【发布时间】:2010-09-30 01:03:16
【问题描述】:
在 C# 控制台应用程序中,是否有一种巧妙的方法可以将控制台输出镜像到文本文件?
目前我只是在日志方法中将相同的字符串传递给Console.WriteLine 和InstanceOfStreamWriter.WriteLine。
【问题讨论】:
在 C# 控制台应用程序中,是否有一种巧妙的方法可以将控制台输出镜像到文本文件?
目前我只是在日志方法中将相同的字符串传递给Console.WriteLine 和InstanceOfStreamWriter.WriteLine。
【问题讨论】:
这可能需要更多的工作,但我会反过来。
为控制台实例化一个TraceListener,为日志文件实例化一个;此后在您的代码中使用Trace.Write 语句而不是Console.Write。之后删除日志、控制台输出或附加其他日志记录机制变得更容易。
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.");
}
据我所知,您可以在应用程序配置中定义侦听器,从而可以在不接触构建的情况下激活或停用日志记录。
【讨论】:
这是一个简单的类,它继承了 TextWriter 以允许将输入重定向到文件和控制台。
这样使用
using (var cc = new ConsoleCopy("mylogfile.txt"))
{
Console.WriteLine("testing 1-2-3");
Console.WriteLine("testing 4-5-6");
Console.ReadKey();
}
这是课程:
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;
}
}
}
【讨论】:
Console.WriteLine() 的现有调用,这正是我想要的。
Console.SetOut(doubleWriter);。这是修改控制台的全局,我花了一点时间,因为我已经习惯于在几乎没有全局的应用程序中工作。好东西!
one.Flush() 和 one.Write(value) 以将文件 IO 排除在等式之外,它仍然会减慢速度。
查看log4net。使用 log4net,您可以设置控制台和文件附加程序,它们可以使用单个日志语句将日志消息输出到两个位置。
【讨论】:
您不能使用> 命令将输出重定向到文件吗?
c:\>Console.exe > c:/temp/output.txt
如果需要镜像,可以尝试查找tee的win32版本,将输出拆分为文件。
请参阅https://superuser.com/questions/74127/tee-for-windows 以从 PowerShell 运行 tee
【讨论】:
您可以子类化 TextWriter 类,然后使用 Console.SetOut 方法将其实例分配给 Console.Out - 这与将相同字符串传递给 log 方法中的两个方法的作用相同。
另一种方式可能是声明您自己的 Console 类并使用 using 语句来区分这些类:
using Console = My.Very.Own.Little.Console;
要访问您需要的标准控制台:
global::Console.Whatever
【讨论】:
编辑:此方法提供了重定向来自第三方包的控制台信息的可能性。重写 WriteLine 方法对我的情况很好,但是您可能需要重写其他 Write 方法取决于第三方包。
首先我们需要创建 StreamWriter 固有的新类,比如 CombinedWriter;
然后用 Console.Out 初始化一个新的 CombinedWriter;
最后我们可以通过 Console.SetOut 将控制台输出重定向到新类的时刻;
以下代码是适合我的新类。
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);
}
}
【讨论】:
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);。否则,如果您使用 WriteLine(format, ...) 方法,它不会打印到控制台。
【讨论】:
我认为您已经使用的是最好的方法。一种从本质上反映您的输出的简单方法。
首先在开头声明一个全局TextWriter:
private TextWriter txtMirror = new StreamWriter("mirror.txt");
然后制作一个写法:
// Write empty line
private void Log()
{
Console.WriteLine();
txtMirror.WriteLine();
}
// Write text
private void Log(string strText)
{
Console.WriteLine(strText);
txtMirror.WriteLine(strText);
}
现在,不要使用Console.WriteLine("...");,而是使用Log("...");。就那么简单。它甚至更短!
如果你移动光标位置(Console.SetCursorPosition(x, y);)可能会有些麻烦,但其他效果很好,我自己也用它!
当然,如果你不只使用 WriteLines,你也可以用同样的方式为 Console.Write(); 创建一个方法
【讨论】:
正如 Arul 所建议的,使用 Console.SetOut 可用于将输出重定向到文本文件:
Console.SetOut(new StreamWriter("Output.txt"));
【讨论】:
使用继承自 StreamWriter 的类的决定以及用户 Keep Thinking 的建议是有效的。 但我必须添加到构造函数 base.AutoFlush = true:
{
this.console = console;
base.AutoFlush = true;
}
以及对析构函数的显式调用:
public new void Dispose ()
{
base.Dispose ();
}
否则,文件在他记录所有数据之前就被关闭了。
我将它用作:
CombinedWriter cw = new CombinedWriter ( "out.txt", true, Encoding.Unicode, 512, Console.Out );
Console.SetOut (cw);
【讨论】:
感谢 Keep Thinking 提供出色的解决方案!我添加了一些进一步的覆盖以避免记录某些控制台写入事件(出于我的目的),这些事件仅用于控制台显示。
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();
}
}
}
【讨论】:
如果您从您无法控制的代码(例如 3rd 方库)复制控制台输出,则应覆盖 TextWriter 的所有成员。代码使用了这个线程的想法。
用法:
using (StreamWriter writer = new StreamWriter(filePath))
{
using (new ConsoleMirroring(writer))
{
// code using console output
}
}
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);
}
}
}
【讨论】:
您实际上可以通过实现您自己的继承自 TextWriter 的类并覆盖 WriteLine 方法来创建 Console.Out 到 Trace 的透明镜像。
在 WriteLine 中,您可以将其写入 Trace,然后可以将其配置为写入文件。
我发现这个答案很有帮助: https://stackoverflow.com/a/10918320/379132
它确实对我有用!
【讨论】:
我的答案是基于most-voted non-accepted answer 和least-voted answer,我认为这是迄今为止最优雅的解决方案。就您可以使用的流类型而言,它更通用一些(例如,您可以使用MemoryStream),但为了简洁起见,我省略了latter answer 中包含的所有扩展功能。
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);
}
}
用法:
using (var stream = File.Create(Path.Combine(Path.GetTempPath(), AppDomain.CurrentDomain.FriendlyName)))
using (var writer = new ConsoleMirrorWriter(stream))
{
// Code using console output.
}
【讨论】: