使用$ INPUT_RECORD_SEPARATOR作为正则表达式读取perl文件句柄

jos*_*sch 5 perl

我正在寻找一种逐行读取文件句柄的方法(然后在每一行上执行一个函数),具有以下转折:我想要视为"行"的东西应该通过改变字符来终止,而不仅仅是我定义为的单个字符$/.我现在$INPUT_RECORD_SEPARATOR$/不支持正则表达式或传递一个字符列表作为行终止符,这就是我的问题所在.

我的文件句柄来自进程的stdout.因此,我无法在文件句柄内寻找并且完整内容不能立即获得,而是在执行过程时逐位产生.我希望能够使用我handler在示例中调用的函数将时间戳附加到进程生成的每个"行" .每一行都应该在程序生成后立即处理.

不幸的是,我只能提出一种方法,既可以handler立即执行函数,但看起来非常低效,或者使用缓冲区的方式,但只会导致handler函数的"分组"调用,从而产生错误的时间戳.

事实上,在我的具体情况下,我的正则表达式甚至会非常简单并且只是阅读/\n|\r/.因此,对于这个特殊问题,我甚至不需要完整的正则表达式支持,而只需要将多个字符视为行终止符.但$/不支持这一点.

有没有一种有效的方法来解决Perl中的这个问题?

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

逐字节读取输入文件句柄

这看起来像这样:

my $acc = "";
while (read($fd, my $b, 1)) {
    $acc .= $b;
    if ($acc =~ /someregex$/) {
        handler($acc);
        $acc = "";
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的优点是,handler一旦读取了足够的字节,就会立即调度.缺点是,我们执行字符串追加并检查我们读取的每个字节的正则表达式$fd.

一次读取带有X字节块的输入文件句柄

这看起来像这样:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的优点是,我们更高效,因为我们只检查每个$bufsize字节.缺点是执行handler必须等到$bufsize读取字节.

nwe*_*hof 2

将 $INPUT_RECORD_SEPARATOR 设置为正则表达式不会有帮助,因为 Perlreadline也使用缓冲 IO。诀窍是使用第二种方法,但使用 unbufferedsysread而不是read. 如果您sysread来自管道,则一旦数据可用,调用就会返回,即使整个缓冲区无法被填充(至少在 Unix 上)。