What's the most defensive way to loop through lines in a file with Perl?

前端 未结 3 1775
粉色の甜心
粉色の甜心 2021-01-01 22:14

I usually loop through lines in a file using the following code:

open my $fh, \'<\', $file or die \"Could not open file $file for reading: $!\\n\";
while          


        
3条回答
  •  旧巷少年郎
    2021-01-01 22:50

    While it is correct that the form of while (my $line=<$fh>) { ... } gets compiled to while (defined( my $line = <$fh> ) ) { ... } consider there are a variety of times when a legitimate read of the value "0" is misinterpreted if you do not have an explicit defined in the loop or testing the return of <>.

    Here are several examples:

    #!/usr/bin/perl
    use strict; use warnings;
    
    my $str = join "", map { "$_\n" } -10..10;
    $str.="0";
    my $sep='=' x 10;
    my ($fh, $line);
    
    open $fh, '<', \$str or 
         die "could not open in-memory file: $!";
    
    print "$sep Should print:\n$str\n$sep\n";     
    
    #Failure 1:
    print 'while ($line=chomp_ln()) { print "$line\n"; }:',
          "\n";
    while ($line=chomp_ln()) { print "$line\n"; } #fails on "0"
    rewind();
    print "$sep\n";
    
    #Failure 2:
    print 'while ($line=trim_ln()) { print "$line\n"; }',"\n";
    while ($line=trim_ln()) { print "$line\n"; } #fails on "0"
    print "$sep\n";
    last_char();
    
    #Failure 3:
    # fails on last line of "0" 
    print 'if(my $l=<$fh>) { print "$l\n" }', "\n";
    if(my $l=<$fh>) { print "$l\n" } 
    print "$sep\n";
    last_char();
    
    #Failure 4 and no Perl warning:
    print 'print "$_\n" if <$fh>;',"\n";
    print "$_\n" if <$fh>; #fails to print;
    print "$sep\n";
    last_char();
    
    #Failure 5
    # fails on last line of "0" with no Perl warning
    print 'if($line=<$fh>) { print $line; }', "\n";
    if($line=<$fh>) { 
        print $line; 
    } else {
        print "READ ERROR: That was supposed to be the last line!\n";
    }    
    print "BUT, line read really was: \"$line\"", "\n\n";
    
    sub chomp_ln {
    # if I have "warnings", Perl says:
    #    Value of  construct can be "0"; test with defined() 
        if($line=<$fh>) {
            chomp $line ;
            return $line;
        }
        return undef;
    }
    
    sub trim_ln {
    # if I have "warnings", Perl says:
    #    Value of  construct can be "0"; test with defined() 
        if (my $line=<$fh>) {
            $line =~ s/^\s+//;
            $line =~ s/\s+$//;
            return $line;
        }
        return undef;
    
    }
    
    sub rewind {
        seek ($fh, 0, 0) or 
            die "Cannot seek on in-memory file: $!";
    }
    
    sub last_char {
        seek($fh, -1, 2) or
           die "Cannot seek on in-memory file: $!";
    }
    

    I am not saying these are good forms of Perl! I am saying that they are possible; especially Failure 3,4 and 5. Note the failure with no Perl warning on number 4 and 5. The first two have their own issues...

提交回复
热议问题