ps_chou
ps_chou

Reputation: 79

how to include grep in a regex in perl

so i'm currently stuck on this problem: 1. i declare a constant list, say LIST 2. i want to read through a file, which i do so line by line in a while loop, and if the line has a keyword from LIST, i print the line, or so something with it.

this is what i have currently:

use constant LIST => ('keyword1', 'keyword2', 'keyword3');
sub main{
    unless(open(MYFILE, $file_read)){die "Error\n"};
    while(<MYFILE>){
        my $line = $_;
        chomp($line);
        if($line =~ m//){#here is where i'm stuck, i want is if $line has either of the keywords
            print $line;
        }
    }
}

What should i do in that if statement to match what i want the program to do? and can i do so without having the $line variable and simply using $_? i only used $line because i thought grep would automatically place the constants in LIST into $_. Thanks!

Upvotes: 0

Views: 463

Answers (1)

David W.
David W.

Reputation: 107040

The easiest way is to define a quoted regular expression as your constant instead of a list:

use strict;
use warnings;
use autodie;    # Will kill program on bad opens, closes, and writes
use feature qw(say);   # Better than "print" in most situations

use constant {
   LIST => qr/keyword1|keyword2|keyword3/, # Now a regular expression.
   FILE_READ => 'file.txt', # You're defining constants, make this one too.
};

open my $read_fh, "<", FILE_READ;  # Use scalars for file handles

# This isn't Java. You don't have to define "main" subroutine

while ( my $line = <$read_fh> ) {
    chomp $line;
    if ( $line =~ LIST ) {  #Now I can use the constant as a regex
        say $line;
    }
}
close $read_fh;

By the way, if you don't use autodie, the standard way of opening a file and failing if it doesn't open is to use the or syntax:

open my $fh, "<", $file_name or die qq(Can't open file "$file_name": $!);

If you have to use a list as a constant, then you can use join to make the regular expression:

use constant LIST => qw( keyword1 keyword2 keyword3 );

...

my $regex = join "|", map LIST;
while ( my $line = <$file_fh> ) {
    chomp $line;
    if ( $line =~ /$regex/ ) {
        say $line;
    }
}

The join takes a list (in this case, a constant list), and separates each member by the string or character you give it. I hope your keywords contain no special regular expression characters. Otherwise, you need to quote those special characters.


Addendum

my $regex = join '|' => map +quotemeta, LIST; – Zaid

Thanks Zaid. I didn't know about the quotemeta command before. I had been trying various things with \Q and \E, but it started getting too complex.

Another way to do what Zaid did:

my @list = map { quotemeta } LIST;
my $regex = join "|", @list;

The map is a bit difficult for beginners to understand. map takes each element in LIST and runs the quotemeta command against it. This returns list which I assign to @list.

Imagine:

use constant LIST => qw( periods.are special.characters in.regular.expressions );

When I run:

my @list = map { quotemeta } LIST;

This returns the list:

my @list = ( "periods\.are", "special\.characters", "in\.regular\.expressions" );

Now, the periods are literal periods instead of special characters in the regular expression. When I run:

my $regex = join "|", @list;

I get:

$regex = "periods\.are|special\.characters|in\.regular\.expressions";

And that's a valid regular expression.

Upvotes: 2

Related Questions