tags:

views:

73

answers:

2

I'm new to Perl and I have a problem when reading a file line by line. I started with a tutorial which suggested using a while loop. That worked fine, however I wanted to have an option to break out of the loop in case of an error. I know I can use the "last" keyword, but I don't like that very much, I would like to include it in the while loop's boolean expression so it is easier to see on what conditions it stops. So I did

$error=0;
while ( (!$error) && (<MYFILE>) ) {
print $_;
...    
}

Unfortunately, this doesn't work because $_ seems to contain a "uninitialized value". For some reason, when I change (!$error) to (!0), it works again. I would understand that it was not working had I used || because of lazy evaluation, but with && in this case both sides need to be evaluated so I don't understand why it doesn't initialize the line variable.

+5  A: 

The magical assignment to $_ only occurs if there is nothing else in the loop:

while (<MYFILE>) {
    print $_;
    ...    
}

If you want the error test too - which is not recommended - then you need to do the assignment:

my $error = 0;
while (!$error && ($_ = <MYFILE>)) {
    print;
    $error = 1 if m/quit/i;
}

Don't forget to add:

use warnings;
use strict;

The reason why the version with '!0' worked is probably because Perl optimizes and recognizes that '!0' is always true and removes it from the loop, leaving just the I/O operator, which then assigns to $_.

NB: it is far better to use idiomatic Perl and the last statement:

while (<MYFILE>) {
    print;
    last if m/quit/i;
}
Jonathan Leffler
Matthew Wilson
Ok, thanks, I didn't know about the nature of the loop. I thought it would improve readability if all conditions for the loop are in one place. At university, I was taught GOTO and - to a lesser extend - break statements are evil, so I have always tried to avoid them. But I will use the version you suggested.
bufferUnderrun
@Matthew: no, you don't need to do that. The line also contains a newline (to be removed by `chomp` when required). I suppose if a file ends with newline followed by 0 (and no trailing newline), then you might be correct - but that's not a standard text file.
Jonathan Leffler
@Matthew Wilson: The `defined` check is done automatically. See http://perldoc.perl.org/perlop.html#I/O-Operators
Michael Carman
+3  A: 

First, as Jonathan points out you should always write languages idiomatically unless you have a very good reason and here you don't.

Your larger problem is misunderstanding the while loop. The automatic assignment of the result of the angle operator to $_ only occurs if it is the only thing in the while conditional test. If there are multiple items in the conditional test they are evaluated and then discarded (Programming Perl pp. 80-81 and I/O Operators at Perldoc).

HerbN