Spot the Bug

Ovid on 2005-09-08T17:22:13

Late at night, lots of coding and a little test program confused me for a moment until I looked at it again and almost laughed myself silly. I needed to test that I could read individual records in a file. The records are separated by a lone dot "." appearing by itself on a line. My intention was to open the file, read in individual records, remove that trailing dot and visually inspect the records one by one. Spot the bug:

#!/usr/bin/perl

use strict;
use warnings;

my $file = shift || die "No file, dummy!";
open FH, "<", $file or die "Could not open ($file) for reading: $!";
local $/ = "\n.\n";

while (defined (my $record = )) {
    chomp $record;
    print $record;
    ;
}

Moral of this story: don't code while tired.


That's a fun bug!

Mr. Muskrat on 2005-09-08T17:53:34

I stared at it and stared at it but didn't see it until I actually tried the code. Then it hit me; "$/ has been modified so it's hanging at the <STDIN>!" Thanks for a very fun lunchtime distraction.
#!/usr/bin/perl
use strict;
use warnings;

my $file = shift || die "No file, dummy!";
open FH, "<", $file or die "Could not open ($file) for reading: $!";
my @records;

{
  local $/ = "\n.\n";
  chomp(@records = <FH>);
};

for my $record (@records) {
    print $record;
    <STDIN>;
}

Re:That's a fun bug!

Ovid on 2005-09-08T17:58:44

Yup. That's it. In my case, I was reading in a huge file so I didn't want to slurp it all in at once. I went for the following fix:

{
  local $/ = "\n";
  <STDIN>;
}

Re:That's a fun bug!

Mr. Muskrat on 2005-09-08T18:11:15

Believe it or not that is what I did first. There's so many different ways that you could handle this situation and that's the beauty of Perl.

Re:

Aristotle on 2005-09-09T01:39:40

Hehe. I did spot it without running the code – after I stepped out of the box and started thinking about what things it does other than its primary goal. It took about three minutes of staring.

I can’t think of a solution that is compact, non-obfuscatory, and avoids redundancy (ie setting $/ twice) all at once. The best I can do is

while( 1 ) {
    do {
       local $/ = "\n.\n";
       my $record = <FH>;
       last if not defined $record;
       chomp $record;
       print $record;
    };
    <STDIN>;
}

The tricky bit in reformulating this is that the new record separator has to be in scope for both the readline as well as the chomp. Perl5 is definitely kludgy here – this should be a property of the filehandle, not a global variable.

Re:

Ovid on 2005-09-09T01:43:30

this should be a property of the filehandle, not a global variable.

Perl 6, baby, Perl 6.