in reply to Re^2: Loop through array or filehandle
in thread Loop through array or filehandle

I do not know if this is any better for your purpose, but you could create an iterator so that the loop uses the iterator and does not care where the data comes from (the iterator handles that part)

Just a quick example:

use warnings; use strict; open my $fh, '<', 'file.txt' or die $!; my @lines = ( "this is line 1; foo \n", "this is line 2: bar \n", "this is line 3: foobar \n", ); sub create_iter { my $arg = shift; if (ref $arg eq 'GLOB') { return sub {<$arg>}; } elsif (ref $arg eq 'ARRAY') { my $index = 0; return sub {$arg->[$index++]} } else { die "Unknown type\n"} } print "PRINTING FROM FH\n"; parse_line($fh); print "\n\nPRINTING FROM ARRAY\n"; parse_line(\@lines); sub parse_line { my $arg = shift; my $iter = create_iter($arg); while (my $val = $iter->()) { print $val if $val =~ /foo/; } }
This prints out this:
PRINTING FROM FH this is line 1; foo this is line 3: foobar this is line 4; foobaz PRINTING FROM ARRAY this is line 1; foo this is line 3: foobar

Replies are listed 'Best First'.
Re^4: Loop through array or filehandle
by markdibley (Sexton) on Sep 30, 2016 at 09:47 UTC

    Thanks again. I think you have come up with the simplest way to do this, although I modified the code slightly (below - doesn't run but is a simplified example of what I was doing). Originally I was wondering if there was a built-in technique that I was missing, but it appears not.

    Thanks again

    #!/usr/bin/perl -ws use strict; open my $fh, '<', 'file.txt' or die $!; my @lines = ( "this is line 1; foo \n", "this is line 2: bar \n", "this is line 3: foobar \n", ); print "PRINTING FROM FH\n"; working_routine($fh); print "\n\nPRINTING FROM ARRAY\n"; working_routine(\@lines); sub create_iter { my $arg = shift; if (ref $arg eq 'GLOB') { return <$arg>; } elsif (ref $arg eq 'ARRAY') { return shift @$arg; } else { die "Unknown type\n"} } sub working_routine{ my ($data) = @_; my $flag = 0; while(my $line = create_iter($data)){ if($line =~ /matches something/){ next; } elsif($line =~ /matches something else/ and ! $flag){ create_iter($data); my $newline = create_iter($data); dosomethingto($newline); } elsif($line =~ /gotcha/ && $flag){ morework($line); } else{ next; } } }
      The call to create_iter should not be in the while loop, create_iter should be called only once, to obtain the right iterator.

      What should go into the while condition is the call to the iterator returned by create_iter (i.e. $iter->() in my sample code).

Re^4: Loop through array or filehandle
by markdibley (Sexton) on Sep 30, 2016 at 13:55 UTC
    Interesting! Thanks. I get it.
Re^4: Loop through array or filehandle
by markdibley (Sexton) on Sep 30, 2016 at 15:42 UTC

    Interestingly, data from an HTTP::Request, when split by \n into an array, only loops to the first empty line and then the while loop stops. If the data from the request is written to a file then the while loop works as expected.

    The only work around I can find at the moment is to

    $data =~ s/^$/ /gsm

    Not ideal, but better than working out why an empty line from an HTTP::Request GET signals a termination of a while() loop.

      In Perl, an empty string is deemed to be false, therefore it will stop the while loop.

      The while loop should probably be amended to check for definedness rather than truth. I cannot test now on my mobile device, but will come back with a proposed solution as soon as I get a chance to test it.

      I.e.:

      my $iter = create_iter(...); ... while (defined(my $line = $iter->())) { do_something_with($line); }


      Give a man a fish:  <%-{-{-{-<

        Yeah, that's exactly what I was thinking about, but I did not want to suggest anything without testing.