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

Greetings, fellow monks.

I'm trying to use perl to replace lines with different patterns in a text file. I'm using the m/// function to match the pattern and then write everything BUT the pattern to a new file.

I'm being sucessfull with one single pattern. Is there a way to do a multiple match? Let's say for an example that I wanna match lines that contain "balls" and "rings".

Is it possible to put it all in a single m///? If don't how can I achieve this?

I know that if you want to match multiple letters you can use something like:
m/[abc]/
And it will match any of these letters, but how to do it with words?

Thanks a lot for your patience, brothers.

Best regards,

Er Galvão Abbott
a.k.a. Lobo, DaWolf
Webdeveloper

Replies are listed 'Best First'.
Re: Multiple matching using m///
by chipmunk (Parson) on Sep 17, 2001 at 04:27 UTC
    Here's the best way I know to match a string which contains a substring AND another substring:
    $_ = 'I threw the balls through the rings and won a prize!'; print "Gotcha!\n" if /^(?=.*balls)(?=.*rings)/s;
    The first lookahead will match 'balls' anywhere in the string; the second will match 'rings' anywhere in the string. Note that it doesn't matter whether 'balls' or 'rings' appears first.

    If you want to match another substring, you can just add another lookahead assertion.

    I recall that I picked this one up from Abigail.

Re: Multiple matching using m///
by wog (Curate) on Sep 17, 2001 at 04:24 UTC
    The easiest way to detect if something matches two patterns is to just try both pattern matches, for example:

    if ($line =~ m/balls/ and $line =~ m/rings/) { # ... }

    This can be done with one regex, too, but it's much more complicated (and probably slower) and more difficult to maintain, for example:

    m/balls.*?rings|rings.*?balls/s

    update: A better technique to combine the two regexes into one is presented in chipmunk's reply to this node. It's not only more maintainable, but it's probably faster then my regex here, at least.

    update 2: I have benchmarked my combined regex, chipmunk's regex, and just using two regexes. (All tuned for matching "rings" and "balls" of course.) Using two regexes is signigantly faster (by 30% for the string "ringsballs" with that factor getting much larger the longer the searched string is.) My combined regex tends to be the slowest of them all, and chipmunk's comes in second. (though performance was close to my combined regex when the string being searched started with one of the strings "rings" or "balls" and there was a lot of junk text in the string being matched.)

    (The benchmark was performed on strings composed of a number of random characters, followed by "rings" followed by a number of random characters followed by "balls" followed by a number of random characters, and the same without the first set of random characters. Random characters were all selected from lowercase letters a through z.)

Re: Multiple matching using m///
by blakem (Monsignor) on Sep 17, 2001 at 04:20 UTC
    perhaps you should be using the s/// operator instead...
    #!/usr/bin/perl -wT use strict; my $line = "I like 'balls' and 'rings', do you like 'rings' and 'balls +'?"; print "Before = $line\n"; $line =~ s/(balls|rings)//g; print "After = $line\n"; =output Before = I like 'balls' and 'rings', do you like 'rings' and 'balls'? After = I like '' and '', do you like '' and ''?

    Update: Apparently the question is an AND one, not an OR one (which is what I provided an answer to). In general, I think mutiple regexes are best for this sort of thing. i.e. if (/balls/ && /rings/) { } there are tricky ways of doing this in one regex, but they tend to be slower, and less readable.

    -Blake

Re: Multiple matching using m///
by Zaxo (Archbishop) on Sep 17, 2001 at 04:24 UTC

    You want alternation to match one of two or more words, m/(balls)|(rings)/.

    Update: Phooey, balls AND rings.

    m/balls/ && m/rings/ && your_action();

    After Compline,
    Zaxo

Re: Multiple matching using m///
by dragonchild (Archbishop) on Sep 17, 2001 at 17:01 UTC
    You want to get every line that does NOT have any of the given patterns? This sounds like a job for grep...
    my @patterns = (qr/balls/, qw/rings/); my @file = <IN_FILE>; my @to_print; foreach my $line (@file) { push @to_print, $line unless grep { $line =~ /$_/ } @patterns; } print OUT_FILE @to_print;

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.