http://www.perlmonks.org?node_id=1044653

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

Hey monks!

I am trying to just get a regex to work but it doesn't do what I am looking for. I want to match anything that has 'neighbor' but not 'use neighbor'. Also, what is before 'use neighbor' varies so I can't really rely on that.

Here is my test code:

#!/usr/bin/perl use strict; use warnings; my $var = "use neighbor 2001:504:0:4::6181:1"; ($var =~/(?!use) neighbor[[:alpha:]-]* ([[:alnum:]\.-:]+)/) and do{ print $1; };

Any ideas would be greatly appreciated! Thanks and have a nice day!

jrvd

Replies are listed 'Best First'.
Re: Quick Regex trouble
by kennethk (Abbot) on Jul 16, 2013 at 20:47 UTC
    Close; you need a negative look-behind assertion:
    #!/usr/bin/perl use strict; use warnings; my $var = "use neighbor 2001:504:0:4::6181:1"; ($var =~/(?<!use )neighbor[[:alpha:]-]* ([[:alnum:]\.-:]+)/) and do{ print $1; };

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      NOTEI think it's actually look-behind assertion he needs.

      ($var =~/(?<=use )neighbor[[:alpha:]-]* ([[:alnum:]\.-:]+)/) and do{ print $1; };
      output 2001:504:0:4::6181:1 If that is the output the OP wanted.

      Because with "negative look-behind assertion", the match is not successful (failed). In fact, there is no output.
      Checked with use re 'debug';
      .. produces ..


      UPDATE:
      NOTE: I think I got this wrong, and kennethk was right! Since, the OP gave a condition on which the regex should match as "when the string doesn't have USE" and the sample he gave has it.

      If you tell me, I'll forget.
      If you show me, I'll remember.
      if you involve me, I'll understand.
      --- Author unknown to me

        I think it's actually look-behind assertion he needs.

        I don't think so. The OP said that it should match "neighbor" but not "use neighbor", so that a zero-width negative look-behind assertion seems to be what is needed to exclude "use".

        Just an additional note: the do {} block is unnecessary in the OP code. The solution could be:

        ($var =~/(?<!use )neighbor[[:alpha:]-]* ([[:alnum:]\.-:]+)/) and print $1;

        or even:

        print $1 if $var =~/(?<!use )neighbor[[:alpha:]-]* ([\d:]+)/;
Re: Quick Regex trouble
by Not_a_Number (Prior) on Jul 16, 2013 at 21:47 UTC

    kennethk++ is right. I'd just like to post a word of warning concerning this part of your regex:

    [[:alnum:]\.-:]+

    Firstly, you don't need to escape the dot in a character class (but there's nothing stopping you from doing so...). More importantly, the sequence

    \.-:

    (with or without the backslash) in a character class means "any character between '.' (dot) and ':' (colon) (inclusive)".

    As it happens, the only things in the ASCII (or UTF, or whatever) table between these two characters are the forward slash and the digits 0-9 (the latter being included, of course, in [:alnum:]).

    The point is, that if you want to put a literal hyphen in a character class, you should generally put it at the very beginning or the very end, or else escape it with a backslash.

    Update: Typos (yawn)

Re: Quick Regex trouble
by choroba (Cardinal) on Jul 17, 2013 at 07:51 UTC
    Just a note: What should the programme do if a line contains both? E.g.
    qwerty neighbor 12345 use neighbor
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: Quick Regex trouble
by hdb (Monsignor) on Jul 17, 2013 at 07:22 UTC

    I would just add another test:

    use strict; use warnings; my $var = "use neighbor 2001:504:0:4::6181:1"; ($var !~ /use neighbor/) and ($var =~/neighbor[[:alpha:]-]* ([[:alnum: +]\.-:]+)/) and do{ print $1; };

    especially as I am not as literate with (negative)? look(back|ahead|around) assertions.