tags:

views:

102

answers:

2

I know the right way to do this if I have Perl 5.10 is to use named captures and values %+, but in Perl 5.8.9 and how can I get a list of successful captures? I have come up with two methods that are both just terrible:

#you need to list each possible match
my @captures = grep { defined } ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);

and

#ew, I turned on symbolic references 
{
    no strict 'refs';
    my @captures = map { defined $+[$_] ? $$_ : () } 1 .. $#+;
}

There is a third option I have found involving (?{}), but it requires global variables (because the closure happens at compile time) and takes the regex from reasonably clear to ungodly mess.

The only alternative I have found is to capture the whole match and then use another set of regexes to get the values I want (actually I build the first regex out of the other regexes because there is no good reason to duplicate the logic).

I obvious left out an important piece of information. I am using the regex in scalar context along with the \G assertion because the regex can change between matches (one of the tokens changes the way you grab tokens off the string). For an example of the code written for Perl 5.10, see this question, specifically this answer.

+1  A: 

You can use @+ and @- as in

substr $var, $-[N], $+[N] - $-[N] # corresponds to $N

But as said earlier, if you can, use the @list = grep defined, $var =~ /regex/ form.

Qtax
not in 5.8.4 you can't, and you don't need grep. Also, he needs to use `\G`.
vladr
Sure you do if you only want the defined once. Eg: `@x = 'foobaz' =~ /foo(bar)?(baz)/;` and `$x[0]` is undefined.
Qtax
I have an example of this in the regex chapter for [Mastering Perl](http://oreilly.com/catalog/9780596527242). It's much more pleasing than trying to eval things. :)
brian d foy
A: 

The following solution uses string eval but in a fairly safe way I think.

Update: Maybe I am still missing something, but AFAICS, the fact that the pattern is using \G and the match is in scalar context only matters because the results of the match cannot be assigned to @matches directly.

In fact, the method below is a variation of the second proposed method in Chas.' OP where he used symbolic references. IMHO, either using symbolic references or string eval is fine because they happen in a very well defined way.

#!/usr/bin/perl

use strict; use warnings;

my $text = <<EOT;
a
2 b
3 c 3
EOT

my $re = qr/([a-z])/;

while ( $text =~ /$re/g  ) {
    my @matches = grep defined, map eval "\$$_", 1 .. $#-;
    print "@matches\n";
    if ( $matches[0] eq 'a' ) {
        $re = qr/\G\s+([0-9])\s+([a-z])/;
        next;
    }
    if ( defined $matches[1] and $matches[1] eq 'b' ) {
        $re = qr/\G\s+([0-9])(?: ([a-z]))(?: ([0-9]))/;
        next;
    }
}

Output:

C:\Temp> jj
a
2 b
3 c 3
Sinan Ünür
The main problem is the fact that he has to use `\G` so no, I don't think your solution will work.
vladr