jasper8989 has asked for the wisdom of the Perl Monks concerning the following question:

Greetings,

Pardon my ignorance. Used to write perl code for just this type of thing but haven't in over 10+ years! Have the occasion to get back into writing perl code and suspect I am making an obvious, basic mistake.

I have an array populated with zip codes. I want to read a text file and only print (output) the lines from the text file that have a zip codes from my array of zip codes.

# first populate array with zipcodes from zipcodes # text file. # --- No problem here my $file = "zipcodes.txt"; open (FH, "< $file") or die "Can't open $file for read: $!"; my @zipcodes; while (<FH>) { push (@zipcodes, $_); } close FH or die "Cannot close $file: $!"; # open the text file for processing looking only for # lines of text with a zipcode from the array @zipcodes # --- here is the problem as it outputs nothing while (my $line = <STDIN>) { foreach my $zipcode (@zipcodes) { if ($line =~ m/$zipcode/) { print $line; } } }

Have searched the site for doing just this, but examples all seemed to focus on elegance, efficiency and alternate ways to do it, which confused me even more as I am relearning perl. Would seem to be simple enough to do but scratching my head. Efficiency not an issue on this for me. Just need it to work, even if not terribly efficient.

Thank you for any insights. Jasper

Replies are listed 'Best First'.
Re: regex matching array values with foreach
by Athanasius (Archbishop) on Sep 09, 2015 at 06:47 UTC

    Hello jasper8989, and welcome to the Monastery!

    Your code actually works fine for me. But note that each $zipcode and $line terminates in a newline character. It is better practice to chomp each line of input to remove the terminal newline. It’s possible that your problem stems from a newline mismatch between the lines in the first and second loops.

    So, you should begin by verifying that @zipcodes contains the data you expect it to. Print out the contents immediately following the first while loop. See Data::Dumper or (my preference) Data::Dump. Likewise, check the contents of $line on each iteration of the second while loop.

    Here are two useful references: Basic debugging checklist and Unbelievably Obvious Debugging Tip.

    Update: Added the second reference.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: regex matching array values with foreach
by dsheroh (Monsignor) on Sep 09, 2015 at 08:36 UTC
    Welcome back to Perl!

    While you were away, the best practice for using open has improved and it is now strongly recommended to use lexical filehandles and the three-argument version of open. In other words, instead of saying

    open (FH, "< $file") or die "Can't open $file for read: $!";
    it's better to use
    open (my $fh, "<", $file) or die "Can't open $file for read: $!";
    In the new version, $fh is scoped like a regular variable (in the old way, FH is global) and passing < and $file as separate parameters helps to prevent some types of potential attacks by malicious users.

    Also, a couple other general tips, which hopefully won't add to your confusion:

    • To read in all the lines of your zipcode file, you can use my @zipcodes = <$fh>; instead of the while loop.
    • You can combine all the zipcodes into a single regular expression with my $zip_re = join '|', @zipcodes; and then test each $line against that single regex instead of looping through @zipcodes each time.
Re: regex matching array values with foreach
by 1nickt (Canon) on Sep 09, 2015 at 06:45 UTC

    You probably want to use a hash instead. Create a key for each zipcode by assigning it the value of 1, then see if the zipcode you're checking has a key in the hash.

    #!/usr/bin/perl use strict; use warnings; my $file = "zipcodes.txt"; my %zipcodes; open (my $fh, '<', $file) or die "Can't open $file for read: $!"; while (<$fh>) { chomp; $zipcodes{$_}++; } close $fh or die "Cannot close $file: $!"; while (chomp( my $line = <STDIN> )) { print "found $line\n" if $zipcodes{ $line }; } __END__
    Don't forget to chomp the lines as you read them from the file, and from STDIN.

    Edit: If you want to be lazy, you can use a module to open, read and close your file, do the chomping for you and put the lines in a list. In the snippet below we use Path::Tiny, which allows us to write one line to return a list of zip codes and map them into the hash:

    use Path::Tiny; my $file = 'index.txt'; my %zipcodes = map { $_, 1 } path( $file )->lines({chomp => 1});
    More simply, this is how to get the list of chomped lines:
    my @lines = path( $file )->lines({chomp => 1});

    Hope this helps!

    The way forward always starts with a minimal test.

      Thanks guys! Chomping the newline did it, nice one!

      Jasper