Hey, Monks, I love your show. Long-time reader, first time meditator.

From time to time, the need to match things not preceded by a pattern rears its head, and the natural tool for that is negative lookbehind. But if the pattern is variable-length, Perl's regex engine can't handle it. This limitation has, at times, resulted in bruised foreheads.

Here are a couple of recipes for working around that limitation. They're not pretty, but they should be plug-and-play.

Matching

# capture all two\d that are not preceded by fo+ $_ = 'footwo1 bootwo2 ftwo3 fotwo4'; $not = qr/fo+/; $want = qr/two\d/; print "Got $1\n" while /\G(?:(?!$not?$want).|$not$want)*($want)/g;
You define variables for what you want and what you don't want to precede it, and just use the pattern as it appears above.

Substituting

Substituting is similar, but because it's not real lookbehind, you have to avoid throwing away the lead-up part of the pattern. Let's say we want to replace all $want (where not preceded by $not) with X. You can capture the lead-up and re-insert it:
s/\G((?:(?!$not?$want).|$not$want)*)($want)/$1X/g;
Or you can match and substitute separately:
s/\G$want/X/ while /\G(?:(?!$not?$want).|$not$want)*(?=$want)/g;
If you want to do a single, rather than global replacement, change the while to an if. Leave the /g on the match -- it is necessary for setting the \G marker.

Note: Regexp::Keep would be very handy here, but this blew up for me:

s/\G((?:(?!$not?$want).|$not$want)*)\K($want)/X/g; # yielded Eval-group not allowed at runtime, use re 'eval' in regex m/\G((?:(?!( +?-xism:fo+)?(?-xism:two\d)).|(?-xism:fo+)(?-xism:two\d))*)(?{Regexp:: +Keep::KEEP})((?-xism:two\d))/ at try2.pl line 9.

Replies are listed 'Best First'.
Re: Variable-length negative lookbehind
by kvale (Monsignor) on May 07, 2004 at 16:54 UTC
    Another technique is to match the reverse of the string. Variable length lookbehinds become variable length lookaheads, which are legal.

    -Mark

      But you're hosed if you've got both lookahead and lookbehind. And I just hate all the reversing.:-)

      The PerlMonk tr/// Advocate
Re: Variable-length negative lookbehind
by flyingmoose (Priest) on May 07, 2004 at 17:35 UTC
    Variable-length negative lookbehind
    <Dave Barry> Would make a great name for a rock band! </Dave Barry>
Re: Variable-length negative lookbehind
by japhy (Canon) on May 07, 2004 at 20:17 UTC
    I'm obsessed with reversing regexes. Simple-enough ones can be reversed easily enough in Perl. It's automating it in C which can be tricky.
    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;
Re: Variable-length negative lookbehind
by dragonchild (Archbishop) on May 07, 2004 at 17:05 UTC
    Or, you can use multiple variable-length look-behinds. Which is what I ended up doing, actually.

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

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      You can only use multiple fixed-length look-behinds if you have a discrete set of alternatives. You couldn't represent (?!<fo+) that way.

      As it turns out, a single positive look-behind was sufficient for your problem.


      The PerlMonk tr/// Advocate