Разбор выходных данных из процесса, который обновляет одну консольную строку - PullRequest
4 голосов
/ 30 сентября 2011

Приветствую членов stackoverflow,

в BackgroundWorker интерфейса WPF. Я запускаю sox (инструмент обработки звука консоли с открытым исходным кодом) в System.Diagnostics.Process.Таким же образом я использую несколько других инструментов командной строки и анализирую их выходные данные для разбивки индикаторов выполнения в моем веб-интерфейсе.

Это отлично работает для других инструментов, но не для Sox, так как вместо спама новые строки для каждого шага прогресса, он обновляет одну строку в консоли, используя только возврат каретки (\ r) и отсутствие перевода строки (\ n).Я пробовал как асинхронное, так и синхронное чтение на process.StandardError.

Использование async process.ErrorDataReceived += (sender, args) => FadeAudioOutputHandler(clip, args); в сочетании с process.BeginErrorReadLine(); не приводит к каким-либо отдельным обновлениям статуса, поскольку по какой-то причине возврат каретки не вызывает ReadLine, дажехотя документы MSDN предполагают, что так и должно быть.Когда процесс завершается, вывод выводится в один блок.

Затем я попробовал следующий код для синхронного 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 байт.Итак, в заключение все, что мне нужно было сделать, это вручную очищать stderr в sox.c после каждого fprintf(stderr, ...) вызова в display_status(...):

fflush(stderr);

Хотя я не уверен, что это где-то близко кэлегантное решение.

Спасибо Эрику Дитриху за ответ, который заставил меня взглянуть на это с другой стороны.

Ответы [ 3 ]

9 голосов
/ 10 октября 2011

Ситуация, которую вы описываете, является известной проблемой - решение, включающее исходный код, см. http://www.codeproject.com/KB/threads/ReadProcessStdoutStderr.aspx

. Оно решает обе проблемы (взаимоблокировка и проблема с \n) ...

2 голосов
/ 12 октября 2011

Это немного глупо, но, возможно, вы могли бы перенаправить вывод неработающего процесса в процесс, который ничего не делает, кроме как обрабатывает ввод с помощью символов, вставляет перевод строки и пишет в стандартный вывод ... Итак, с точки зрения (очень) псевдокод:

StartProcess("sox | littleguythatIwrote")
ReadStandardOutTheWayYouAleadyAre()

Может быть, это просто перемещает стойку ворот (я намного лучше знаком с std in / out / err в мире NIX), но это другой способ выглядетьво всяком случае, в проблеме.

2 голосов
/ 11 октября 2011

Мне приходилось сталкиваться с подобной проблемой с помощью специального инструмента для сборки в visual studio. Я обнаружил, что использование регулярного выражения и выполнение синтаксического анализа в том же потоке, что и чтение, является проблемой, и обработка вывода прерывается. В итоге я получил стандартное решение для производителей, где вы читаете строки из вывода и помещаете их в очередь. Затем очередь будет снята с очереди и обработана в другом потоке. Я не могу предложить исходный код, но у этого сайта есть фантастические ресурсы: http://www.albahari.com/threading/part2.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...