在单个控制台行上处理输出重定向

时间:2015-11-15 04:58:07

标签: c# asynchronous redirectstandardoutput

我正在编写一个C#应用程序,它可以启动第三方命令行可执行文件并将其标准输出重定向到RichTextBox。

我可以使用process.OutputDataReceived事件重定向输出。我有一个队列,我将行推送到,然后我使用计时器定期将所有排队的行转储到RichTextBox。

这适用于某些第三方程序。

问题在于多次向控制台的单行写入的程序。例如:5% - > 10% - > 25%等...(不断改写同一个地方) 我想这是通过不添加换行符来完成的,而是简单地使用回车符回到行的开头并写入前面的字符。

如果OutputDataReceived似乎没有任何方式指示是否应该应用新行,那么我的RichTextBox输出只会在新行上有多个更新。 例如: 5% 10% 25% 等...

有没有办法判断标准输出是在新线还是在同一条线上?

2 个答案:

答案 0 :(得分:5)

如果我理解正确,您当前会将进度更新报告给RichTextBox控件作为单独的行,而您希望模拟控制台窗口的行为。即使用新的输出行覆盖以前的输出行,而不是保留上一行并添加新行。考虑到这一点......

.NET类型,包括TextReaderProcess类对输出的处理等内容,将换行符视为\r\n或{{ 1}}。因此,即使只是使用\r\n来覆盖当前行的过程仍然会被\r解释为开始新行。鉴于这种行为在.NET中的一致性,您唯一真正的选择是避免使用任何内置的基于行的I / O机制。

幸运的是,您可以通过OutputDataReceived直接从流程中获取异步读取,而不必处理基于行的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); }); 中,该字符串将传递给处理每个读取回调的匿名方法。因此,您可以自己扫描该字符串,查找指示您正在开始新行的单独回车符,但应在添加新文本之前删除上一行。确保首先添加(或至少跳过)所有文本到回车符,然后然后删除最后一行,然后再添加新文本。

答案 1 :(得分:1)

此代码将修复原始输出并使其显示在终端上。

 //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());
}