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

I need to find the length of the first captured match from a regex. Normally, I might do this:
my $x = 'abc12345'; my ($y) = $x =~ /(\d+)$/; my $length = length($y);
Being in a contrary mood, I wondered if I could do that in one statement, and tried this:
my $x = 'abc12345'; my $length = length ($x =~ /(\d+)$/); # returns 1, the length of the number in the number of elements
I came up with this, but it wasn't satisfying:
my $x = 'abc12345'; my $length = map { length } ($x =~ /(\d+)$/);
(Yes, useles use of map might deserve its own warning.)

To generalize for cases where there might be more matches (a variable in the regex, for example), I generalized to this:

my $x = 'abc12345'; my $length = map { length } ($x =~ /(\d+)$/)[0];
But that led me to this:
my $x = 'abc12345'; my $length = length (($x =~ /(\d+)$/)[0]); # returns 5
Is there a better way to do this, or have I hit the practical limit?

-QM
--
Quantum Mechanics: The dreams stuff is made of

Replies are listed 'Best First'.
Re: Length of first captured match
by Tanktalus (Canon) on May 05, 2006 at 23:58 UTC

    my $length = $x =~ /(\d+)$/ ? length $1 : 0;
    While I think this is what you're looking for, I'm just guessing at what the right answer is if there is no match.

      While I think this is what you're looking for, I'm just guessing at what the right answer is if there is no match.
      Actually, there will always be a match, but it's appropriate to worry about null matches.

      -QM
      --
      Quantum Mechanics: The dreams stuff is made of

Re: Length of first captured match
by GrandFather (Saint) on May 06, 2006 at 00:01 UTC

    It may be that this is what you are after:

    'abc12345' =~ /(\d+)$/; print $+[1] - $-[1];

    Prints:

    5

    DWIM is Perl's answer to Gödel
Re: Length of first captured match
by japhy (Canon) on May 06, 2006 at 03:56 UTC
    You said:
    my $x = 'abc12345'; my $length = length ($x =~ /(\d+)$/); # returns 1, the length of the number in the number of elements
    That's not true. length() imposes scalar context on its argument, so the pattern match happens in scalar context and returns "1" on success and "" on failure. Thus, you're getting length(1), which is 1, not "length of the number in the number of elements", because "number of elements" is not what the regex is returning.

    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
Re: Length of first captured match
by rhesa (Vicar) on May 06, 2006 at 00:04 UTC
    my $x = 'abc12345'; my $length = $x =~ /(\d+)$/ && length($1); print "the number in $x is $length long"; __END__ the number in abc12345 is 5 long
    update or what Tanktalus said... I'm beginning to see the attraction of having First Post! ;)
Re: Length of first captured match
by ikegami (Patriarch) on May 06, 2006 at 00:51 UTC
    local our $length; $x =~ /(\d+)(?{ $length = length($1) })$/;

    or in Perl 5.8:

    local our $length; $x =~ /(\d+)(?{ $length = length($^N) })$/;
Re: Length of first captured match
by NetWallah (Canon) on May 06, 2006 at 06:32 UTC
    Another way - force array context on the regex, make an anon array ref, dereference, and shift - this gets you the first element (or zero'th index).
    Once you have that, "length" is easy. Code does not look as bad as it sounds ..
    my $len = length shift @{ [ $x =~ /(\d+)$/ ,'' ] };
    The additional ",''" is necessary to satisfy "use warnings" - which would otherwise complain about "Use of uninitialized value in length at line xx" in the case that the match was not found.
    In this case, the empty string arrives, available to be "shift"ed, allowing "length" to evaluate the length of the empty string, instead of an undef value.

         "For every complex problem, there is a simple answer ... and it is wrong." --H.L. Mencken

      ${[...]}[0]
      is much less efficient and harder to read than the list slice
      (...)[0]
      the OP proposed. The advatage is that yours can be embedded into an interpolating string.

Re: Length of first captured match
by NetWallah (Canon) on May 06, 2006 at 00:36 UTC
    Here is a minor variation of Tanktalus' solution. I prefer this because it leaves $length as "undef" on no match.
    my $length = length $1 if $x =~ /(\d+)$/;

         "For every complex problem, there is a simple answer ... and it is wrong." --H.L. Mencken