1

In Perl, how do you find the index of the last digit in a string?

Example: Hello123xyz index of last digit is 7

Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223

3 Answers3

5

A RE to match the last digit in the string and the @- variable to get the index of the start of the match:

#!/usr/bin/env perl
use warnings;
use strict;
use feature qw/say/;

sub last_digit_index($) {
    if ($_[0] =~ /\d\D*\z/) {
        return $-[0];
    } else {
        return -1;
    }
}

say last_digit_index("Hello123xyz"); # 7
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • Nice, (++) , what is `($)` please? – Gilles Quénot Feb 16 '23 at 12:18
  • 1
    @GillesQuénot A prototype. See https://perldoc.perl.org/perlsub#Prototypes for details. – Shawn Feb 16 '23 at 13:00
  • 2
    See also [Why are Perl 5's function prototypes bad?](https://stackoverflow.com/questions/297034/why-are-perl-5s-function-prototypes-bad) – Dave Cross Feb 16 '23 at 14:31
  • 1
    And I'm not sure why you'd go to all the effort of writing your own version of [`index()`](https://perldoc.perl.org/functions/index). – Dave Cross Feb 16 '23 at 14:32
  • 1
    @DaveCross My own version of index? Huh? This does something pretty different from that function. – Shawn Feb 16 '23 at 14:39
  • Perl *could* use a version of `index` that searches right to left for the last match. And then use it ten times for all 10 digits and take their maximum... might be useful for some other task though, but not this. – Shawn Feb 16 '23 at 14:41
  • The right to left version of `index` is `rindex` – brian d foy Feb 16 '23 at 15:11
  • Of course it is. Silly me, expecting it to be mentioned in the documentation for `index`. Still useless here as already noted. – Shawn Feb 16 '23 at 15:16
  • Yeah, sorry. Typo in my comment (twice actually, because it's in the link too!) – Dave Cross Feb 16 '23 at 16:35
3

I'd probably use the pos function here. Match with /g and the Perl remembers where the match left off. The next global match on that string will start where the last match left off, so isolate this in a sub or block to avoid weird effects on subsequent matches on the same variable.

Since the position counts from 0, the next position will be one greater than the 1-based position of the final digit. You decide if you want to subtract 1 or not:

use v5.10;

say last_digit_pos('Hello123xyz');

sub last_digit_pos {
    my( $string ) = @_;
    $string =~ m/^.*\d/sg;
    return pos($string);  # 6
    }

And, if the string doesn't match, pos doesn't return a defined value.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
3

Can also leverage List::MoreUtils::last_index

use List::MoreUtils qw(last_index);

my $last_digit_index = last_index { /[0-9]/ } split '', $string;

I find this simple: break the string into a list of characters with a typical use of split, and use a library to find the last one which is a digit, via a trivial regex.

Note that this is "expensive" as it creates a scalar for each character and runs regex multiple times. So if efficiency matters -- if this is done on an absolutely gigantic string, or many many many times on smaller strings -- then better seek other approaches, or at least benchmark before deciding.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • This is a lot more expensive as it involves creating many scalars, multiple sub calls and multiple regex evaluations. – ikegami Feb 17 '23 at 04:30
  • @ikegami "_expensive_" -- well, yes, fully agree. But I guess that that won't matter and wanted to draw attention to a way that doesn't involve a lot more complex regex manipulations/properties but is rather simple (conceptually), in comparison. Thanks for bringing it up though, I should've stated that. Adding a comment... – zdim Feb 17 '23 at 07:28