在单个控制台行上处理输出重定向
本文关键字:处理 输出 重定向 单个 控制台 | 更新日期: 2023-09-27 18:22:06
我正在编写一个C#应用程序,它可以启动第三方命令行可执行文件,并将其标准输出重定向到RichTextBox。
我能够通过使用该进程重定向输出。OutputDataReceived事件。我有一个将行推送到的队列,然后我使用计时器定期将所有排队的行转储到RichTextBox中。
这非常适用于某些第三方程序。
问题在于程序多次写入控制台的单行。例如:5%->10%->25%等(不断重写同一点)我想,这是通过不放换行符来完成的,而是简单地返回到带有回车符的行的开头,并重写前面的字符。
结果是OutputDataReceived似乎没有任何方式来指示是否应该应用新行,所以我的RichTextBox输出只是在新行上有多个更新。例如:5%10%25%等等。
有什么方法可以判断标准输出是在新行还是在同一行?
如果我理解正确,您当前看到的进度更新报告给RichTextBox
控件是单独的行,而您希望模拟控制台窗口的行为。即用新的输出行覆盖以前的输出行,而不是保留前一行并添加一个新的。考虑到这一点…
.NET类型,包括TextReader
和Process
类对输出的处理,将换行视为'r
、'n
或'r'n
中的任何一个。因此,即使是简单地使用'r
来覆盖当前行的过程,OutputDataReceived
仍然会将其解释为开始新的行。考虑到这种行为在.NET中的一致性,您唯一真正的选择就是避免使用任何内置的基于行的I/O机制。
幸运的是,您可以直接通过StandardOutput
从进程中获得异步读取,而不必处理基于行的OutputDataReceived
事件。例如:
async Task ConsumeOutput(TextReader reader, Action<string> callback)
{
char[] buffer = new char[256];
int cch;
while ((cch = await reader.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
callback(new string(buffer, 0, cch));
}
}
你可以这样称呼它:
// don't "await", but do keep "task" so that at some later time, you can
// check the result, "await" it, whatever, to determine the outcome.
Task task = ConsumeOutput(process.StandardOutput, s =>
{
richTextBox1.AppendText(s);
});
当然,在没有任何修改的情况下,上面的内容只会做你已经得到的。但是以这种方式读取输出不会对换行字符进行任何解释。相反,它们将出现在传递给处理每个读取回调的匿名方法的字符串s
中。因此,您可以自己扫描该字符串,寻找单独的回车符,该回车符表示您正在开始新行,但应在添加新文本之前删除前一行。请确保先将所有文本添加(或至少跳过)到回车,然后删除最后一行,再添加新文本。
此代码将修复原始输出并使其与终端上显示的一样。
//Deletes all lines that end with only a CR - this emulates the output as it would be seen cmd
public static string DeleteCRLines( this string Str) {
Str = Str.Replace( "'r'r'n", "'r'n");
//Splits at the CRLF. We will end up with 'lines' that are full of CR - the last CR-seperated-line is the one we keep
var AllLines = new List<string>( Str.Split( new[] {"'r'n"}, StringSplitOptions.None));
for (int i = 0; i < AllLines.Count; i++){
var CRLines = AllLines[i].Split(''r');
AllLines[i] = CRLines[ CRLines.Count() -1];
}
return String.Join( Environment.NewLine, AllLines.ToArray());
}