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

C# 在单个控制台行上处理输出重定向,c#,asynchronous,redirectstandardoutput,C#,Asynchronous,Redirectstandardoutput,我正在编写一个C#应用程序,它可以启动第三方命令行可执行文件,并将其标准输出重定向到RichTextBox 我可以使用process.OutputDataReceived事件重定向输出。我有一个将行推送到的队列,然后使用计时器定期将所有排队的行转储到RichTextBox中 这对于某些第三方程序非常有效 问题在于多次写入控制台单行的程序。例:5%->10%->25%等。。。(不断重写同一点) 我想,这是通过不放置换行字符来实现的,而只是简单地返回到行的开头,使用回车符并覆盖前面的字符 发生的情况

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

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

这对于某些第三方程序非常有效

问题在于多次写入控制台单行的程序。例:5%->10%->25%等。。。(不断重写同一点) 我想,这是通过不放置换行字符来实现的,而只是简单地返回到行的开头,使用回车符并覆盖前面的字符

发生的情况是OutputDataReceived似乎没有任何方式来指示是否应该应用新行,因此我的RichTextBox输出只是在新行上有多个更新。 如: 5% 10% 25% 等等


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

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

< > > .NET类型,包括代码>文本TXTRADADER 和进程类的输出处理,考虑换行为<代码> > 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));
    }
}

当然,没有任何修改,上面的内容只会实现您已经获得的功能。但以这种方式读取输出不会对换行符进行任何解释。相反,它们将出现在字符串
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());
}
//删除所有仅以CR结尾的行-这将模拟输出,就像看到的一样
公共静态字符串DeleteCRLines(此字符串Str){
Str=Str.Replace(“\r\r\n”和“\r\n”);
//在CRLF分离。我们将以充满CR的“线”结束-最后一条CR分离线是我们保留的
var AllLines=new List(Str.Split(new[]{“\r\n”},StringSplitOptions.None));
for(int i=0;i
是否以二进制打开输入?可能需要,如果没有,那么你可以看到控制字符。哦,有趣。。。我使用事件中的DataReceivedEventArgs获取字符串。我没有意识到你可以用进程打开一个流。标准输出。。。我需要对此进行试验。
过程。StandardOutput本身不是一个流,而是一个解码流中数据的
TextReader
。但是,是的,你可以直接阅读;请参阅我在下面发布的答案。谢谢,这段代码工作得很好,但是要真正获得正确的输出,还需要相当多的额外代码。由于字符以长流形式出现,因此不能假定一行将以\r\n或\r结尾,因此需要逐个检查字符,并确定是否要换行。这可能也很方便:如果来自流程的响应并不总是基于行的,那么这种方法也很有用。我的进程返回任意数量的数据行(每行都有一个换行符),然后返回一个提示(没有换行符)。我需要捕捉“提示”以知道何时可以发送下一个数据请求。
 //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());
}