PHP:尝试使fgets()在CRLF,CR和LF上同时触发 [英] PHP: trying to get fgets() to trigger both on CRLF, CR and LF

查看:85
本文介绍了PHP:尝试使fgets()在CRLF,CR和LF上同时触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用proc_open和fgets($ stdout)在PHP中读取流,试图获取每一行.

I'm reading streams in PHP, using proc_open and fgets($stdout), trying to get every line as it comes in.

许多linux程序(程序包管理器,wget,rsync)仅对行使用CR(回车)字符,这些字符会定期就地"更新,例如下载进度.我想一看到这些更新(作为单独的行).

Many linux programs (package managers, wget, rsync) just use a CR (carriage return) character for lines which periodically updates "in place", like download progress. I'd like to catch these updates (as separate lines) as soon as they happen.

此刻,fgets($ stdout)一直读到LF,所以当进度进行得非常缓慢(例如大文件)时,它将一直读直到完全完成,然后将所有更新的行作为一个返回.长字符串,包括 CR.

At the moment, fgets($stdout) just keeps reading until a LF, so when progress is going very slowly (big file for example) it just keeps on reading until it's completely done, before returning all the updated lines as one long string, including the CRs.

我尝试设置"mac"选项来检测CR作为行尾:

I've tried setting the "mac" option to detect CRs as line endings:

ini_set('auto_detect_line_endings',true); 

但这似乎不起作用.

现在,stream_get_line将允许我将CR设置为换行符,但不能使用全部捕获"解决方案,该解决方案将CRLF,CR和LF都视为分隔符.

Now, stream_get_line would allow me to set CRs as line breaks, but not a "catch all" solution which treats both CRLF, CR and LF as delimiters.

我当然可以阅读整行,使用各种PHP方法将其拆分,并用LF替换所有类型的换行符,但这是一个流,并且我希望PHP能够在其仍在运行时获得进度的指示.

I could of course read the whole line, split it using various PHP methods and replace all types of linebreaks with LFs, but it's a stream, and I want PHP to be able to get an indication of progress while it's still running.

所以我的问题:

如何从STDOUT管道(从proc_open)读取直到出现LF CR,而不必等到整行都进入?

How can I read from the STDOUT pipe (from proc_open) until a LF or CR happens, without having to wait until the whole line is in?

提前谢谢!

我使用Fleshgrinder的过滤器类在流中将\ r替换为\ r(请参见接受的答案),并用fgetc()替换了fgets()以获取对STDOUT内容的更多实时"访问:

I used Fleshgrinder's filter class to replace \r with \n in the stream (see accepted answer), and replaced fgets() with fgetc() to get more "realtime" access to contents of STDOUT:

$stdout = $proc->pipe(1);
stream_filter_register("EOL", "EOLStreamFilter");
stream_filter_append($stdout, "EOL"); 

while (($o = fgetc($stdout))!== false){
    $out .= $o;                            // buffer the characters into line, until \n.
    if ($o == "\n"){echo $out;$out='';}    // can now easily wrap the $out lines in JSON
}

推荐答案

在使用流之前,使用流过滤器对换行符进行规范化.我基于 stream_filter_register .

Use a stream filter to normalize your new line characters before consuming the stream. I created the following code that should do the trick based on the example from PHP’s manual page on stream_filter_register.

<?php

// https://php.net/php-user-filter
final class EOLStreamFilter extends php_user_filter {

    public function filter($in, $out, &$consumed, $closing)
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $bucket->data = str_replace([ "\r\n", "\r" ], "\n", $bucket->data);
            $consumed += $bucket->datalen;
            stream_bucket_append($out, $bucket);
        }
        return PSFS_PASS_ON;
    }

}

stream_filter_register("EOL", "EOLStreamFilter");

// Open stream …

stream_filter_append($yourStreamHandle, "EOL");

// Perform your work with normalized EOLs …


编辑:马克·贝克(Mark Ba​​ker)在您的问题上发表的评论是正确的.大多数Linux发行版都使用 STDOUT 的行缓冲区,Apple可能也这样做.另一方面,大多数 STDERR 流是未缓冲的.您可以尝试将程序的输出重定向到另一个管道(例如 STDERR 或任何其他管道),以查看是否还有更多运气.


The comment Mark Baker posted on your question is true. Most Linux distributions are using a line buffer for STDOUT and it is possible that Apple is doing the same. On the other hand most STDERR streams are unbuffered. You could try to redirect the output of the program to another pipe (e.g. STDERR or any other) and see if you have more luck with that.

这篇关于PHP:尝试使fgets()在CRLF,CR和LF上同时触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆