J
January Weiner
Hello there,
I have a small problem, which I can actually solve quite easily, but I am
looking for a more elegant solution.
I am reading from a pipe
open( my $if, "$cmd|" ) or die "Cannot run $cmd: $!\n" ;
The output from this command is a series of records. Each record starts
with a recognizable line (e.g. I could catch it with /^Name=(\S+)/), but
does not have a "record end" marker. That is, I can only tell that a record
ended when a new record starts or when EOF is reached.
Now, I want to process the records one by one in one subroutine, calling
another one ("parse_record") to read exactly one record and return it:
while( my $record = parse_record( $if ) ) {
# ... do something with $record
}
Normally, when reading a regular file, I do something like this in the
parse_record() function (see below[1] for example code): whenever I read a
line from $if, I store the position in file returned by tell() ; if I find
the beginning of the next record, I seek to the position prior to the
current line and return the current record.
Unfortunately, I can't seek in a pipe. What I do instead is to return two
records ( the "current one", completely read, and the "next one", with a
stub from parsing the "record start" line), and then pass the "next record"
information to the parse_record() subroutine.
This is not elegant, as I would like to have the while() loop above to be
completely unaware of details of the parsing (e.g. I want to use it with
different parsers and file types).
One other solution that I was thinking of is to store this "next record"
line in a static variable. For example, I could make the whole parser OO,
and stored this "buffer" in a private variable.
Any other thing that I could do?
j.
[1] Example code 1:
sub parse_record {
my ( $if ) = @_ ;
my $cur_record ;
my $fpos ;
while( <$if> ) {
if( /^Name=(\S+)/ ) {
if( $cur_record ) { # we already have a record defined
seek $if, $fpos, 0 ; # rewind so that the next instance of
# parse_record
# we know $fpos is defined, because we have
# a previous record
return $cur_record ;
}
$cur_record = { name => $1 } ;
$fpos = tell $if ;
next ;
}
next unless $cur_record ;
$fpos = $cur_record ;
# read the rest of the record here...
}
return $cur_record ;
}
I have a small problem, which I can actually solve quite easily, but I am
looking for a more elegant solution.
I am reading from a pipe
open( my $if, "$cmd|" ) or die "Cannot run $cmd: $!\n" ;
The output from this command is a series of records. Each record starts
with a recognizable line (e.g. I could catch it with /^Name=(\S+)/), but
does not have a "record end" marker. That is, I can only tell that a record
ended when a new record starts or when EOF is reached.
Now, I want to process the records one by one in one subroutine, calling
another one ("parse_record") to read exactly one record and return it:
while( my $record = parse_record( $if ) ) {
# ... do something with $record
}
Normally, when reading a regular file, I do something like this in the
parse_record() function (see below[1] for example code): whenever I read a
line from $if, I store the position in file returned by tell() ; if I find
the beginning of the next record, I seek to the position prior to the
current line and return the current record.
Unfortunately, I can't seek in a pipe. What I do instead is to return two
records ( the "current one", completely read, and the "next one", with a
stub from parsing the "record start" line), and then pass the "next record"
information to the parse_record() subroutine.
This is not elegant, as I would like to have the while() loop above to be
completely unaware of details of the parsing (e.g. I want to use it with
different parsers and file types).
One other solution that I was thinking of is to store this "next record"
line in a static variable. For example, I could make the whole parser OO,
and stored this "buffer" in a private variable.
Any other thing that I could do?
j.
[1] Example code 1:
sub parse_record {
my ( $if ) = @_ ;
my $cur_record ;
my $fpos ;
while( <$if> ) {
if( /^Name=(\S+)/ ) {
if( $cur_record ) { # we already have a record defined
seek $if, $fpos, 0 ; # rewind so that the next instance of
# parse_record
# we know $fpos is defined, because we have
# a previous record
return $cur_record ;
}
$cur_record = { name => $1 } ;
$fpos = tell $if ;
next ;
}
next unless $cur_record ;
$fpos = $cur_record ;
# read the rest of the record here...
}
return $cur_record ;
}