Hi Anonymous,

The problem is that every time you write <>, a new record is read from the file. while (<>) { ... } will assign the current record to the $_ variable, so inside the loop you should just use that instead of <>. Note that when you write a regex without the =~ operator, it will implicitly be tested against the $_ variable.

One way to do this is a state machine type approach, this has the advantages that you only read from <> once, it has no nested loops, and it's very extensible.

use warnings; use strict; use constant { IDLE => 0, IP_ADDRS => 1, }; my $state = IDLE; while (<>) { if ($state == IDLE) { if (/Relay.access.denied/) { $state = IP_ADDRS } } elsif ($state == IP_ADDRS) { if (/(\d+)\s+(\S+)/) { print "$2\n" } else { $state = IDLE } } }

Of course, the nested loops approach is much shorter in this case, I just personally wouldn't do this because if your code gets longer, it will get more complicated and much harder to maintain than the above approach.

use warnings; use strict; while (<>) { if (/Relay.access.denied/) { while (<>) { /(\d+)\s+(\S+)/ or last; print "$2\n"; } } }

Update: For completeness, note that it's also possible to invert the structure of the conditions in the state machine approach, which allows for a bit more error checking. Or, if you don't care about those conditions, they can be removed from the following code to make it shorter (about as long as the above state machine code). You can choose whether you want to use the above approach or the following one based on which one makes your logic easier to express. In this case it's roughly equivalent, but if your code gets longer that might change.

use warnings; use strict; use constant { IDLE => 0, IP_ADDRS => 1, }; my $state = IDLE; while (<>) { chomp; if (/Relay.access.denied/) { if ($state == IDLE) { $state = IP_ADDRS } elsif ($state == IP_ADDRS) { warn "unexpected: $_" } } elsif (/(\d+)\s+(\S+)/) { if ($state == IDLE) { warn "unexpected: $_" } elsif ($state == IP_ADDRS) { print "$2\n" } } else { if ($state == IDLE) { } elsif ($state == IP_ADDRS) { $state = IDLE } } }

Hope this helps,
-- Hauke D


In reply to Re: Loop problem: jumps one record (updated) by haukex
in thread Loop problem: jumps one record by Anonymous Monk

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.