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

Dear Monks,

I've a very long text and I need to pick out some values. However I'm only interested in the last result. Example:
#! /usr/bin/perl $str = <<HERE ; abc 10 abc 11 abc 12 abc 13 abc 14 HERE ($dum) = $str =~/abc\s(\d+)/gs ; print "dum is $dum\n" ;
Result: dum is 10
But I would like it to hold 14.
I can fix this with:
$dum  = ($str =~/abc\s(\d+)/gs)[-1] ;
Is there an other way within the expression to do this ?

Thanks in advance
Luca

Replies are listed 'Best First'.
Re: REGEXP: only need last matching string
by inman (Curate) on Dec 23, 2005 at 12:05 UTC
    $1 will contain the last match if you execute the regexp in list context.
    (my $dum) = $str =~/abc\s(\d+)/g ; print "dum is $1\n" ;
    gives
    dum is 14
      Then
      () = $str =~ /abc\s(\d+)/gs;
      will suffice, i.e. no need for the $dum variable.
Re: REGEXP: only need last matching string
by prasadbabu (Prior) on Dec 23, 2005 at 12:16 UTC

    TIMTOWTDI

    #! /usr/bin/perl $str = <<HERE ; abc 10 abc 11 abc 12 abc 13 abc 14 HERE ($dum) = $str =~/abc\s(\d+)$/; print "dum is $dum\n" ;

    Prasad

Re: REGEXP: only need last matching string
by salva (Canon) on Dec 23, 2005 at 12:05 UTC
    try this:
    ($dum) = $str =~ /^.*abc\s(\d+)/s
    though, I am not sure about the eficience of this regular expresion.

      When I read this, I thought "Don't be ridiculous! Yours is probably the most efficient method suggested!" Then I set about benchmarking the various suggestions to prove it. Alas, yours is only the second most efficient (in terms of time):

                     Rate inman blazar2 blazar1 jeanluca thedoe2 prasadbabu drmoron salva thedoe1
      inman      107580/s    --     -7%    -28%     -33%    -51%       -54%    -72%  -77%    -78%
      blazar2    115313/s    7%      --    -23%     -28%    -48%       -51%    -70%  -75%    -76%
      blazar1    149209/s   39%     29%      --      -7%    -32%       -37%    -61%  -68%    -69%
      jeanluca   160157/s   49%     39%      7%       --    -27%       -32%    -58%  -65%    -67%
      thedoe2    220552/s  105%     91%     48%      38%      --        -6%    -43%  -52%    -54%
      prasadbabu 235856/s  119%    105%     58%      47%      7%         --    -39%  -49%    -51%
      drmoron    384090/s  257%    233%    157%     140%     74%        63%      --  -16%    -20%
      salva      459627/s  327%    299%    208%     187%    108%        95%     20%    --     -5%
      thedoe1    481345/s  347%    317%    223%     201%    118%       104%     25%    5%      --
      
        I can see NO reason why thedoe1 should be performing any better than salva's code.
        salva => sub { (my $dum) = $str =~ /^.*abc\s(\d+)/s; }, thedoe1 => sub { (my $dum) = $str =~ /(?<!abc).*abc\s(\d+)/gs; },
        Those regexes are equivalent. In fact, I can't understand why in the world thedoe used a look-behind there. It accomplishes nothing, since the first place the regex tries to match is at the beginning of the string. The only difference that it could make is if pos($str) is something other than 0, and then that means it would not necessary operate properly (insofaras what was requested from the regex). Sorry to sound grumpy, but this is a misuse of a look-behind (and the /g modifier) that I think should be pointed out. There's no voodoo going on.

        Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
        How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
        well, you are benchmarking a corner case, that's when the last abc \d+ is at the end of the string.

        When I said that I didn't know about its performance I was thinking on some not so convenient case, for instance, if the matching substr is near the beginning, in that case perl regexp engine is going to backtrack a lot and maybe looping with the OP regexp and discarding all but the last match could perform better.

Re: REGEXP: only need last matching string
by blazar (Canon) on Dec 23, 2005 at 12:22 UTC
    (my $dum) = reverse $str =~/abc\s(\d+)/gs; print "dum is $dum\n"; 1 while $str =~/abc\s(\d+)/gs; print "dum is $1\n";
Re: REGEXP: only need last matching string
by doctor_moron (Scribe) on Dec 23, 2005 at 12:31 UTC
    #! /usr/bin/perl $str = <<HERE ; abc 10 abc 11 abc 12 abc 13 abc 14 HERE ($dum) = $str =~/\d+$/gs ; print "dum is $dum\n" ;

    does it work ?

    zak

Re: REGEXP: only need last matching string
by thedoe (Monk) on Dec 23, 2005 at 14:26 UTC

    You could use a negative look behind assertion to do this:

    ($dum)  = $str =~/(?<!abc).*abc\s(\d+)/gs;

    To read more about this feature, along with lots more on regex, check out perlre.

    Update: Another way to do this is explained in Roy Johnson's post on look-ahead and look-behind. I knew I had seen this before, but couldn't find it when I was first answering your question.

    /abc(?!.*abc)\s(\d+)/s

    Not sure which is easiest for you to understand, but either way I would recommend reading that post. (Thanks to prasadbabu for linking to it in a later node so I could find it again!)