in reply to Re^3: nested <FILE> read returns undefined?
in thread nested <FILE> read returns undefined?

You said:

I may be missing something (you haven't said what the second loop actually does, so I can't be sure), but couldn't this be better handled with next ?

Imagine a text file that has this format:

image: path/to/image.jpg attribute1: value attribute2: value [...] attributeN: value ---------- image: path/to/next/image.jpg [...]

Here, each "record" ends with a line of dashes. My script is reading the file, and when it reads an "image" line, it sees if that image is one that it knows about, and if not, it skips the rest of the record. So, the code looks like:

while (my $line = <FILE>) { $line =~ /(.*?): (.*?)$/m; ($key, $value) = (lc $1, $2); next if !$key || !$value; if ($key eq "image") { if (!exists $db->{$value}) { # not in the database, so skip this image while ($_ = <FILE>) { last if /^--/; } next; }

The original code used "for" instead of "while" in each spot above, which is why it wasn't working... and why I posted my original message.... I'm just answering why the code is the way it is because you asked. Sure, I could have done it in a way that allowed me to continue using "for", but changing it to use while() was a better option.

Replies are listed 'Best First'.
Re^5: nested <FILE> read returns undefined?
by tirwhan (Abbot) on Apr 01, 2006 at 08:28 UTC

    Thanks for taking the time to explain this. First, there's a bug in your code, because $1 and $2 will not be reset when the while loop starts over, but retain their old values, despite the fact that the match did not succeed (see the warning about this in perldoc perlre). So the line

    next if !$key || !$value;

    will not skip lines that don't contain a key-value pair.

    Here's how I would write this, without using the inner while loop:

    my $curimage; while (my $line = <DATA>) { $line =~ /^([^:]*): (.*?)$/m or next; my ($key, $value) = (lc $1, $2); $curimage = $value if ($key eq "image"); next if(!exists $db->{$curimage}); # ..process key-value pairs }

    This has the obvious disadvantage that you need to look up $db more often, so if $db is not just a hash reference and lookups are more expensive this will be slow. But you can get around that by memoizing $db->{$curimage} and I think the code is clearer this way. YMMV of course.


    All dogma is stupid.
      Your notes were spot-on. Mea Culpa on the $1 and $2 part.

      One caveat to your corrected code was another oversight on my part in my more brief explanation of the rationale for my code segment (which reminded me why I did it the way I did in the first place): each record "may" contain more than one "image: /path..." key-value pair, which is what requires reading ahead to the "----" line before returning to the outside control loop. Your version will just fine the next "image:" key, which would have been the right thing to do if it weren't for that condition.

      Anyway, all's well now.

Re^5: nested <FILE> read returns undefined?
by graff (Chancellor) on Apr 02, 2006 at 05:07 UTC
    I gather you have reached a solution based on the replies already posted, but just for the sake of TMTOWTDI, your data would make a nice case for customizing the INPUT_RECORD_SEPARATOR variable, $/, like so:
    { # customize $/, but only inside this code block: local $/ = "----------\n"; while (<FILE>) { chomp; # this removes $/ from the end my ( $image, @attributes ) = split /\n/; $image = s/^image:\s+(.*)/\L($1)/; next unless ( exists $db->{$image} ); # do something with @attributes... } } # now $/ is back to normal # (updated to add chomp call)
    Note that if you are using modules or reading from other files while doing things with the attributes within that block, the altered value of $/ will be in effect -- you may need to create more block boundaries to localize it again back to its original value.