问候stackoverflow会员们,
在WPF前端的BackgroundWorker中,我使用System.Diagnostics.Process
运行sox(开源控制台声音处理工具)。我以同样的方式使用其他几个命令行工具,并解析它们的输出来填充我的前端中的进度栏。
这对于其他工具有效,但对于Sox而言却不是这样,因为它不会为每个进度步骤产生新行,而是只使用回车符(\r)而没有换行符(\n)在控制台上更新单行。我尝试了在process.StandardError
上进行异步和同步读取。
使用process.ErrorDataReceived += (sender, args) => FadeAudioOutputHandler(clip, args);
与process.BeginErrorReadLine();
组合起来不能产生任何单独的状态更新,因为由于某种原因,回车符不会触发ReadLine,即使MSDN文档表明它应该。当进程完成时,输出将一次性输出。
然后,我尝试了以下代码,在流上进行同步的char by 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#端的streamreader直到默认的4096字节缓冲区被填满之前都不会刷新。我通过将每行填充到至少4096个字节来确认了这一点。所以最后的结论就是,在sox.c中的每个fprintf(stderr, …)
调用之后手动刷新stderr即可:display_status(...)
。fflush(stderr);
虽然我不确定这是否是一个优雅的解决方案。
感谢Erik Dietrich的回答,让我从不同角度看待这个问题。