bufferUnderrun
bufferUnderrun

Reputation: 33

Perl while loop / reading file

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.

Upvotes: 2

Views: 10439

Answers (2)

HerbN
HerbN

Reputation: 1509

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).

Upvotes: 3

Jonathan Leffler
Jonathan Leffler

Reputation: 755064

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;
}

Upvotes: 5

Related Questions