http://www.perlmonks.org?node_id=600423

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

I wrote a code that looks like this:

my $text = <<TEXT; Title: The Moor's Last Sigh, Author: Salman Rushdie Title: The God of Small Things, Author: Arundhati Roy TEXT my @answers; my $re = qr/Title: (.*?), Author: (\w+) (\w+)$/; # 3 groups here while ($text =~ /$re/mgc) { my %ans; push @answers, [ $1, $2, $3 ]; # that's the main point! } use Data::Dump qw(dump); print dump(\@answers);

And then I got the answer I want in @answers, namely:

( ["The Moor's Last Sigh", "Salman", "Rushdie"], ["The God of Small Things", "Arundhati", "Roy"], )

Now imagine I want to run the same code against other inputs and with other regexes driving the extraction. These regexes can have an arbitrary number of capturing groups. I can't get all groups by using $text = /$re/mgc in list context, because it will make a loop over the text gathering $1, $2, ... of each match into a list (that is, $1, $2, ..., $1, $2, ..., ...). The code above is actually a simplification and it should work on a context more like this:

if ($text =~ /$re1/mgc) { ... if ($text =~ /$re2/mgc) { ... if ($text =~ /$re1/mgc) { ... if ($text =~ /$re2/mgc) { ...

So the /g behavior in list context is inappropriate. To get ($1, $2, ...) in a generic form, the only way I envisaged was to use @- and @+ and to employ a piece of code like this:

# return ($1, $2, ...) matched against $s sub _groups { my $s = shift; my @groups; foreach my $i (1..$#-) { push @groups, substr($s, $-[$i], $+[$i] - $-[$i]); } return @groups }

and then:

push @answers, [ _groups($text) ]; # [ ($1, $2, ...) ]

My question is: Is there actually a better way to do this?