Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

positive regex for inverted match

by kgimpel (Acolyte)
on Feb 26, 2004 at 06:08 UTC ( [id://331916]=perlquestion: print w/replies, xml ) Need Help??

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

I am passing a positive regex as a string argument to another function, and I'd only like to match on lines that don't contain "bar."

IE:

INPUT looks like this:
foo
"bar" blah
"test" blah
"test2"

MATCHES should be:
foo
"test" blah
"test2"

So, I effectively want to do this
$_ !~ /bar/
but I cannot change the logic to do it.

I've tried
/.*?(?!bat)/
but I think the line taken as a whole,
^.*bar.*$
satifies n-characters not followed by "bar," so we get a positive match. Can anyone suggest pattern for this?

Thanks in advance.

Replies are listed 'Best First'.
Re: positive regex for inverted match
by eyepopslikeamosquito (Archbishop) on Feb 26, 2004 at 07:12 UTC

    This is discussed at length in the Perl Cookbook, 2nd edition, recipe 6.18.

    As to why, it is common for a program to accept a pattern as one of its arguments; in that case you cannot use $_ !~ /bar/ but must instead resort to:

    /^(?:(?!bar).)*$/s
Re: positive regex for inverted match
by I0 (Priest) on Feb 26, 2004 at 06:32 UTC
    /^((?!bar).)*$/s
Re: positive regex for inverted match
by Zaxo (Archbishop) on Feb 26, 2004 at 06:20 UTC

    You say you can't use !~, but you don't really say why not. Does ! /$re/ suit you better?

    my $re = qr/foo/; my @no_match = grep { ! /$re/ } <INPUT>;

    After Compline,
    Zaxo

Re: positive regex for inverted match
by BrowserUk (Patriarch) on Feb 26, 2004 at 08:01 UTC

    If the program that's calling the function is accepting a regex input by a user, requiring them to use '/^(?:(?!bar).)*$/s' when they want to find things that don't contain some word or phrase is not very friendly.

    If the input is simple text, you might get away with passing "/^(?:(?!$their_input).)*$/s", thus wrapping their input in the appropriate construct if they specified 'NOT bar' or '!bar', or some flag to indicate that they want to invert the sense of the comparison. You'd have to strip the flag from the input before calling the function.

    But if your going to have a flag they can use to indicate the inversion of the match, why not simply pass that flag through to the function and use it to determine the operator to use

    sub do_selection{ my( $flag, $match ) = @_; if( $flag ) { do_stuff if $data !~ $match; else { do_stuff if $data =~ $flag; } } my( $flag, $regex) = get_input(); do_selection( $flag, $regex );

    That's the way grep -v works, which is a pretty good precedent.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    Timing (and a little luck) are everything!
      I think that is a good suggestion. But perhaps it would be a good idea to take the flag as the second parameter (and use it optional) like this:
      sub do_selection ($;$) { my $match = shift; my $flag = shift || 0; ...
      In this case you don't have to change lots of other calls of do_selection.
Re: positive regex for inverted match
by kvale (Monsignor) on Feb 26, 2004 at 06:33 UTC
    Here is a regex solution to you problem:
    while (<DATA>) { chomp; print "$_ has no bar\n" if /^([^b]|b[^a]|ba[^r])+$/; } __DATA__ bar foo "bar" blah baz "bar" blah "test" blah "test2"

    -Mark

      Classic++, including classic mistakes. (:

      Your regex will match "bbar" despite it containing "bar". "bb" matches b[^a] and then "a" and "r" each match [^b].

      I started exploring this idea in depth and hope to write a lengthy node on it one day. Trying to fix the problems leads you down paths similar to:

      /^([^b]|b[^a]| ba[^r])+$/ /^([^b]+|b+[^ab]| b+a[^rb])*b*a?$/ /^([^b]+|b+[^ab]|(b+a)+([^rb]|b+[^ab]))*[ba]*$/

      - tye        

Re: positive regex for inverted match
by ysth (Canon) on Feb 26, 2004 at 08:01 UTC
    /^(?!.*bar)/
Re: positive regex for inverted match
by arden (Curate) on Feb 26, 2004 at 06:30 UTC
    Just set the match to be exactly zero times.

    print "$_" if $_ =~ /(bar){0}/;

    - - arden.

    Update: Thanks hossman++, this is proof you shouldn't attempt regexes at 3am when you're sleep-walking.

      Uh, no.

      That will match every string, because "bar" 0 times is the empty string, and every input matches the empty string.

      laptop:~> perl -le 'print "matches" if "bar" =~ /(bar){0}/;' matches laptop:~> perl -le 'print "also matches" if "barbar" =~ /(bar){0}/;' also matches laptop:~> perl -le 'print "event this matches" if "" =~ /(bar){0}/;' event this matches

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://331916]
Approved by arden
Front-paged by arden
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (3)
As of 2024-04-19 15:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found