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

I believe positive lookbehind is supposed to return the "5" in the following but it returns "minutes" instead. Any help deeply appreciated!

my $body_text="there are 5 minutes left in the program"; if ($body_text=~m/\d{1,2}(?=\s(mins|minutes))/g) { print $1; }

Replies are listed 'Best First'.
Re: Return value from positive look behind
by choroba (Cardinal) on Nov 11, 2020 at 17:05 UTC
    What leads you to the belief? The digit is not captured, so it can't be returned.

    Use parentheses to capture it. If you don't want to capture the minutes, don't use capturing parentheses:

    /(\d{1,2})(?=\s(?:mins|minutes))/ # ^ ^ ~~ # | | \ # \ capture don't capture

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      Thank you! Posting the solution that worked (returns 5) for me.

      my $body_text="there are 5 minutes left in the program"; if ($body_text=~m/(\d{1,2})(?=\smins|\sminutes)/g) { print "here: " . $1 . "\n"; }

      @Choroba: Another unusual thing happens if I run the code 2x in a row. The first if{} works but the second does not. Does the regex somehow need to reset?<\p>

      my $body_text="there are 5 minutes left in the program"; if ($body_text=~m/(\d{1,2})(?=\smins|\sminutes)/g) { print "1st try: " . $1 . "\n"; } if ($body_text=~m/(\d{1,2})(?=\smins|\sminutes)/g) { print "2nd try: " . $1 . "\n"; }
        That's because of the /g, it remembers where the match happend last time and tries to match again after it.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

        To illustrate the effect noted in choroba's reply try the following:

        use strict; use warnings; my $body_text="5 minutes left. Wait 6 minutes before nagging again."; if ($body_text=~m/(\d{1,2})(?=\smins|\sminutes)/g) { print "1st try: " . $1 . "\n"; } if ($body_text=~m/(\d{1,2})(?=\smins|\sminutes)/g) { print "2nd try: " . $1 . "\n"; }

        Prints:

        1st try: 5 2nd try: 6
        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Return value from positive look behind (updated)
by AnomalousMonk (Archbishop) on Nov 11, 2020 at 23:12 UTC

    Another example of the effect of what you were doing in the OP (i.e., a non-zero-width capture within a zero-width assertion):

    Win8 Strawberry 5.8.9.5 (32) Wed 11/11/2020 18:02:18 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings -l my $s = 'ABCDE'; while ($s =~ m{ (?= (\w+)) }xmsg) { print $1; }; ^Z ABCDE BCDE CDE DE E
    A useful trick when you want overlapping captures.

    Update: This might be a better example of overlapping capture:

    Win8 Strawberry 5.8.9.5 (32) Wed 11/11/2020 21:41:12 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings -l my $s = 'ABCDE X YZ FGHI'; while ($s =~ m{ (?= (\w{3})) }xmsg) { print "'$1'"; }; ^Z 'ABC' 'BCD' 'CDE' 'FGH' 'GHI'


    Give a man a fish:  <%-{-{-{-<

Re: Return value from positive look behind
by Anonymous Monk on Nov 12, 2020 at 14:26 UTC

    Quibble: this is actually a lookahead, not a lookbehind. A lookbehind would be

    m/(?<=\bare\s)(\d{1,2})/

    Note that, depending on the version of Perl in use, variable-length lookbehinds (e.g. m/(?<=\bare\s{1,3})(\d{1,2})/ are either unavailable, or restricted in how much they can match.

      Variable-length positive lookbehinds of any length can be realized with the \K regex operator introduced with Perl version 5.10; see Lookaround Assertions in perlre. (Update: I should add that \K is mainly useful in s/// substitutions.)


      Give a man a fish:  <%-{-{-{-<