以$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您的解决方案会破坏每一行使用哪一行终止符的信息。因此,处理程序
将无法根据它得到的信息重建原始信息。@simbabqueawk
解决方案无法按预期工作,因为一旦遇到由RS
定义的终止符,它将不会立即打印“行”。试着运行这个:{echo-ne“blubber\r”;sleep 1;echo-n“blabber\r”;sleep 1;echo“foo”}awk“BEGIN{RS=“[\r\n]”FS=“\n”}{print;}”