2011-09-30 50 views
4

问候计算器件,输出解析更新单个控制台线路

在WPF前端的一个BackgroundWorker

我运行在一个System.Diagnostics.Processsox(开源控制台声音处理工具)。以同样的方式,我使用其他几个命令行工具并解析它们的输出以在我的前端填充进度条。

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

结合使用异步process.ErrorDataReceived += (sender, args) => FadeAudioOutputHandler(clip, args);process.BeginErrorReadLine();不会产生任何的个人状态更新,因为某些原因,回车不触发的ReadLine,即使MSDN docs认为它应该。当过程完成时,输出会在一个块中吐出。

我然后通过炭尝试了同步炭以下代码读取的流:

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, ...)通话后display_status(...)

fflush(stderr); 

虽然,我不知道这是任何接近一个完美的解决方案。

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

+0

关于sox的信息让我感到好奇,所以我做了一些调查。这可能是一个远射,但http://en.wikipedia.org/wiki/Setvbuf有有趣的可能性。它看起来像你可以通过文件句柄强制缓冲设置,而不是通过进程。这可以让你'重写'sox的默认行为,这样你就可以独立保存它的源代码,而不会在每次更新它们的东西时注定要手动更新。可能有一个托管的C#等价物,或者你可以只写一个调用一个微小的C实用程序。 –

回答

9

您所描述的情况是一个已知的问题 - 对包括源代码的解决方案看http://www.codeproject.com/KB/threads/ReadProcessStdoutStderr.aspx

它解决了这两个问题(死锁,并与\n问题)......

+0

谢谢你的回答。我使用链接上的ProcessIoManager尝试了相同的结果。输出在开始时挂起,然后输出以块的形式出现,每个块都有5秒的延迟。如果我使用链接中的示例gui和sox结合使用,也会发生同样的情况。 RedirectStandardError和sox输出似乎存在问题。在命令行上运行sox不会显示这些症状。 – Till

+0

5秒延迟是流式缓冲区缓冲区填满的时间,如果感兴趣,请查看我更新的问题。 – Till

+0

感谢您的更新......您描述它的方式是依赖于实现的,所以除了更改SOX之外,我没有看到任何其他选项,因为存在实现导致此行为的实现,并且接受方的任何内容都无法更改。 – Yahia

2

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

+0

这似乎并不是一个问题,即使在注释了状态更新的间隔检查后,每分钟会产生几千行,正则表达式部分也不会引起任何问题。 – Till

2

这是一个有点缺憾,但也许你可以管的不合作过程的输出到一个过程,什么也不做,但过程中输入的字符,插入线饲料,并写入到标准输出。所以,在(非常)伪码方面:

StartProcess("sox | littleguythatIwrote") 
ReadStandardOutTheWayYouAleadyAre() 

难道说只是移动球门柱(我有很多性病/出更熟悉/ Nr世界中的错误),但无论如何,这是查看问题的另一种方式。

+0

谢谢你让我检查东西的sox边。如果您有兴趣,请查看我的最新问题。 – Till

+0

嗯......有趣。我想知道是否可以以某种方式代表另一个进程清除标准错误(似乎不太可能,但是谁知道?)。我能想到的唯一方法是以某种方式欺骗sox,认为它与用户交互。如果内存服务,如果STD I/O的C库认为它们是由用户直接调用的,则它们的行为会有所不同。但是再一次,Linux上的内存就是C,所以要拿下一点盐。 –