以$INPUT\u RECORD\u分隔符作为正则表达式读取perl文件句柄

以$INPUT\u RECORD\u分隔符作为正则表达式读取perl文件句柄,perl,Perl,我正在寻找一种逐行读取文件句柄(然后在每行上执行一个函数)的方法,方法如下:我想要处理的“行”应该以不同的字符终止,而不仅仅是我定义为$/的单个字符。我现在知道$INPUT\u RECORD\u SEPARATOR或$/不支持正则表达式或传递要作为行终止符处理的字符列表,这就是我的问题所在 我的文件句柄来自进程的标准输出。因此,我无法在文件句柄中查找,并且完整内容不能立即获得,而是在执行过程中一点一点地生成。我希望能够使用我在示例中调用的handler函数将时间戳之类的东西附加到流程生成的每一“

我正在寻找一种逐行读取文件句柄(然后在每行上执行一个函数)的方法,方法如下:我想要处理的“行”应该以不同的字符终止,而不仅仅是我定义为
$/
的单个字符。我现在知道
$INPUT\u RECORD\u SEPARATOR
$/
不支持正则表达式或传递要作为行终止符处理的字符列表,这就是我的问题所在

我的文件句柄来自进程的标准输出。因此,我无法在文件句柄中查找,并且完整内容不能立即获得,而是在执行过程中一点一点地生成。我希望能够使用我在示例中调用的
handler
函数将时间戳之类的东西附加到流程生成的每一“行”。每一行都应该在程序生成后立即处理

不幸的是,我只能想出一种方法,要么立即执行
处理程序
函数,但效率极低,要么使用缓冲区,但只会导致
处理程序
函数的“分组”调用,从而产生错误的时间戳

事实上,在我的特定情况下,我的正则表达式甚至非常简单,只需读取
/\n | \r/
。所以对于这个特殊的问题,我甚至不需要完全的正则表达式支持,只需要将多个字符作为行终止符。但是,
$/
不支持此功能

在Perl中有没有有效的方法来解决这个问题

下面是一些快速的伪perl代码来演示我的两种方法:

逐字节读取输入文件句柄 这看起来像这样:

my $acc = "";
while (read($fd, my $b, 1)) {
    $acc .= $b;
    if ($acc =~ /someregex$/) {
        handler($acc);
        $acc = "";
    }
}
my $acc = "";
while (read($fd, my $b, $bufsize)) {
    if ($b =~ /someregex/) {
        my @parts = split /someregex/, $b;
        # for brevity lets assume we always get more than 2 parts...
        my $first = shift @parts;
        handler(acc . $first);
        my $last = pop @parts;
        foreach my $part (@parts) {
            handler($part);
        }
        $acc = $last;
    }
}
这里的优点是,一旦读取了足够的字节,
handler
就会立即被调度。缺点是,我们对从
$fd
读取的每个字节进行字符串追加和检查正则表达式

一次读取X字节块的输入文件句柄 这看起来像这样:

my $acc = "";
while (read($fd, my $b, 1)) {
    $acc .= $b;
    if ($acc =~ /someregex$/) {
        handler($acc);
        $acc = "";
    }
}
my $acc = "";
while (read($fd, my $b, $bufsize)) {
    if ($b =~ /someregex/) {
        my @parts = split /someregex/, $b;
        # for brevity lets assume we always get more than 2 parts...
        my $first = shift @parts;
        handler(acc . $first);
        my $last = pop @parts;
        foreach my $part (@parts) {
            handler($part);
        }
        $acc = $last;
    }
}

这里的优点是,我们只检查每个
$bufsize
字节,因此效率更高。缺点是,
处理程序
的执行必须等到读取了
$bufsize
字节。

将$INPUT\u RECORD\u分隔符设置为正则表达式不会有帮助,因为Perl的
readline
也使用缓冲IO。诀窍是使用第二种方法,但使用无缓冲的
sysread
而不是
read
。如果从管道
sysread
,即使无法填充整个缓冲区(至少在Unix上),调用也会在数据可用时立即返回。

nwellnhof的建议允许我实现此问题的解决方案:

my $acc = "";
while (1) {
    my $ret = sysread($fh, my $buf, 1000);
    if ($ret == 0) {
        last;
    }
    # we split with a capturing group so that we also retain which line
    # terminator was used
    # a negative limit is used to also produce trailing empty fields if
    # required
    my @parts = split /(\r|\n)/, $buf, -1;
    my $numparts = scalar @parts;
    if ($numparts == 1) {
        # line terminator was not found
        $acc .= $buf;
    } elsif ($numparts >= 3) {
        # first match needs special treatment as it needs to be
        # concatenated with $acc
        my $first = shift @parts;
        my $term = shift @parts;
        handler($acc . $first . $term);
        my $last = pop @parts;
        for (my $i = 0; $i < $numparts - 3; $i+=2) {
            handler($parts[$i] . $parts[$i+1]);
        }
        # the last part is put into the accumulator. This might
        # just be the empty string if $buf ended in a line
        # terminator
        $acc = $last;
    }
}
# if the output didn't end with a linebreak, handle the rest
if ($acc ne "") {
    handler($acc);
}
my$acc=”“;
而(1){
my$ret=sysread($fh,my$buf,1000);
如果($ret==0){
最后;
}
#我们与一个捕获组分离,以便我们也保留哪条线
#使用了终结者
#如果出现以下情况,负限制也用于生成尾随空字段:
#必需的
我的@parts=split/(\r |\n)/,$buf,-1;
my$numparts=标量@零件;
如果($numparts==1){
#找不到行终止符
$acc.=$buf;
}elsif($numparts>=3){
#第一场比赛需要特殊处理,因为它需要
#与$acc连接
我的$first=shift@零件;
我的$term=shift@零件;
经办人($acc.$first.$term);
我的$last=pop@零件;
对于(我的$i=0;$i<$numparts-3;$i+=2){
处理器($parts[$i]。$parts[$i+1]);
}
#最后一部分放入累加器。这可能会
#如果$buf以一行结尾,则仅为空字符串
#终结者
$acc=$last;
}
}
#如果输出没有以换行符结束,请处理其余的
如果($acc ne“”){
经办人(行政协调会);
}
我的测试表明,如果输入流中有暂停,甚至在读取1000个字符之前,
sysread
确实会返回。上面的代码注意将多个长度为1000的消息连接起来,并使用较小的长度或多个终止符正确拆分消息


如果您在上述代码中看到任何错误,请大声喊叫。

您如何获得
$fd
?@Саа27这就是我的意思。如果awk中的
RS
是多个字符,则它是一个正则表达式。通过像
awk'BEGIN{RS=“[aeiou]”FS=“\n”}{print;}”
这样的管道将在所有人声上分割,并将行转换成
$/
可以处理的内容。虽然
\n
可能是您希望保留的内容,但请使用铃铛或
\0
或其他任何东西。@simbabque OP也可以通过perl过滤器将其输入导入,
perl-pe'tr |\r |\n |'file | script.pl
,因为他只在寻找
/\r |\n/
分隔符。@simbabque@С||27您的解决方案会破坏每一行使用哪一行终止符的信息。因此,
处理程序
将无法根据它得到的信息重建原始信息。@simbabque
awk
解决方案无法按预期工作,因为一旦遇到由
RS
定义的终止符,它将不会立即打印“行”。试着运行这个:
{echo-ne“blubber\r”;sleep 1;echo-n“blabber\r”;sleep 1;echo“foo”}awk“BEGIN{RS=“[\r\n]”FS=“\n”}{print;}”