开发者

Is there an easy way to get a list of all successful captures from a regex pre-5.10?

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.


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.


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜