解析更新单个控制台行的进程的输出

问候stackoverflow成员,

在WPF Frontend的BackgroundWorker中,我在System.Diagnostics.Process运行sox (开源控制台声音处理工具)。 以同样的方式,我使用其他几个命令行工具并解析他们的输出,以在我的前端poulate进度条。

这适用于其他工具,但不适用于Sox,因为它不是为每个进度步骤发送新行,而是通过仅使用回车符(\ r)和没有换行符(\ n)更新控制台上的单行。 我在process.StandardError上尝试了异步和同步读取。

使用async process.ErrorDataReceived += (sender, args) => FadeAudioOutputHandler(clip, args);process.BeginErrorReadLine();结合使用process.BeginErrorReadLine(); 不会产生任何单独的状态更新,因为由于某种原因,回车不会触发ReadLine,即使MSDN文档建议它应该。 当进程完成时,输出将在一个块中吐出。

然后,我通过流上的char读取尝试了以下代码用于同步char:

 char[] c; var line = new StringBuilder(); while (process.StandardError.Peek() > -1) { c = new char[1]; process.StandardError.Read(c, 0, c.Length); if (c[0] == '\r') { var percentage = 0; var regex = new Regex(@"%\s([^\s]+)"); var match = regex.Match(line.ToString()); if (match.Success) { myProgressObject.ProgressType = ProgressType.FadingAudio //... some calculations omitted for brevity percentage = (int) Math.Round(result); } else { myProgressObject.ProgressType = ProgressType.UndefinedStep; } _backGroundWorker.ReportProgress(percentage, myProgressObject); line.Clear(); } else { line.Append(c[0]); } } 

上面的代码似乎没有实时读取流,但会暂停输出一段时间。 然后它发送了一小块空间,最后在整个过程中途陷入僵局。

任何有关正确方向的提示都将不胜感激!

更新(草率?)解决方案:

这让我抓狂,因为我在C#方面尝试的东西似乎对结果没有任何影响。 我最初的实现,在更改它15次并引入新的依赖项之前,很好。

问题出在sox和RedirectStandardError上。 我发现在获取sox源代码并构建自己的版本之后。 首先我完全删除了sox的所有输出,除了我真正感兴趣的东西,然后将输出更改为实线后跟换行符\n 。 我认为这可以解决我的问题。 好吧,它没有。 我不知道足够的c ++来实际找到原因,但是他们似乎已经调整了stdio如何写入该流,如何缓冲它或以如此特殊的方式执行它,使得c#端的流读取器不会刷新,直到默认4096字节缓冲区已满。 我确认通过将每行填充到至少4096字节。 总而言之,我所要做的就是在display_status(...)每个fprintf(stderr, ...)调用之后手动刷新sox.c中的fprintf(stderr, ...)

 fflush(stderr); 

虽然,我不确定这是否接近优雅的解决方案。

感谢Erik Dietrich的回答让我从不同角度看待这个问题。

您描述的情况是一个已知问题 – 对于包含源代码的解决方案,请参阅http://www.codeproject.com/KB/threads/ReadProcessStdoutStderr.aspx

它解决了两个问题(死锁和\n的问题)……

我不得不在visual studio中使用定制的构建工具处理类似的问题。 我发现使用正则表达式并在与读取相同的线程中进行解析是一个问题,输出处理将停止。 我最终得到了一个标准的消费者生产者解决方案,您可以从输出中读取行并将它们粘贴到队列中。 然后让队列出队并在其他线程上处理。 我不能提供源代码,但这个网站有一些很棒的资源: http : //www.albahari.com/threading/part2.aspx

它有点kludgy,但也许您可以将不合作进程的输出传递给一个进程,该进程除了处理字符输入,插入换行符和写入标准输出之外什么都不做……所以,就(非常)伪而言-码:

 StartProcess("sox | littleguythatIwrote") ReadStandardOutTheWayYouAleadyAre() 

可能只是移动球门柱(我对NIX世界中的std in / out / err更熟悉),但无论如何,这是一种不同的方式来看问题。