Как сохранить записи из регулярного выражения Perl в отдельные переменные?

у меня есть регулярное выражение:

/abc(def)ghi(jkl)mno(pqr)/igs

Как бы я записал результаты каждой круглой скобки в 3 разных переменных, по одной для каждой круглой скобки? Прямо сейчас я использую один массив, чтобы захватить все результаты, они выходят последовательными, но затем я должен проанализировать их, и список может быть огромным.

@results = ($string =~ /abc(def)ghi(jkl)mno(pqr)/igs);

5 ответов


Ваш вопрос немного неоднозначный для меня, но я думаю, что вы хотите сделать что-то вроде этого:

my (@first, @second, @third);
while( my ($first, $second, $third) = $string =~ /abc(def)ghi(jkl)mno(pqr)/igs) {
    push @first, $first;
    push @second, $second;
    push @third, $third;
}

начиная с 5.10, вы можете использовать именованные буферы захвата а также:

#!/usr/bin/perl

use strict; use warnings;

my %data;

my $s = 'abcdefghijklmnopqr';

if ($s =~ /abc (?<first>def) ghi (?<second>jkl) mno (?<third>pqr)/x ) {
    push @{ $data{$_} }, $+{$_} for keys %+;
}

use Data::Dumper;
print Dumper \%data;

выход:

$VAR1 = {
          'first' => [
                       'def'
                     ],
          'second' => [
                        'jkl'
                      ],
          'third' => [
                       'pqr'
                     ]
        };

для более ранних версий вы можете использовать следующее, что позволяет избежать добавления строки для каждого захваченного буфера:

#!/usr/bin/perl

use strict; use warnings;

my $s = 'abcdefghijklmnopqr';

my @arrays = \ my(@first, @second, @third);

if (my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
    push @{ $arrays[$_] }, $captured[$_] for 0 .. $#arrays;
}

use Data::Dumper;
print Dumper @arrays;

выход:

$VAR1 = [
          'def'
        ];
$VAR2 = [
          'jkl'
        ];
$VAR3 = [
          'pqr'
        ];

но мне нравится хранить связанные данные в одной структуре данных, поэтому лучше вернуться к использованию хэша. Для этого требуется вспомогательный массив, однако:

my %data;
my @keys = qw( first second third );

if (my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
    push @{ $data{$keys[$_]} }, $captured[$_] for 0 .. $#keys;
}

или, если имена переменных действительно first, second etc, или если имена буферов не имеют значения, но только порядок, вы можете использовать:

my @data;
if ( my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
    push @{ $data[$_] }, $captured[$_] for 0 .. $#captured;
}

альтернативный способ сделать это будет выглядеть как ответ ghostdog74, но с использованием массива, который хранит хэш-ссылки:

my @results;
while( $string =~ /abc(def)ghi(jkl)mno(pqr)/igs) {
    my ($key1, $key2, $key3) = (, , );
    push @results, { 
        key1 => $key1,
        key2 => $key2,
        key3 => $key3,
    };
}

# do something with it

foreach my $result (@results) {
    print "$result->{key1}, $result->{key2}, $result->{key3}\n";
}

с главным преимуществом здесь использования одиночной структуры данных, и иметь славный читаемый цикл.


@OP, когда скобки захвачены, вы можете использовать переменные $1,$2....эти обратные ссылки

$string="zzzabcdefghijklmnopqrsssszzzabcdefghijklmnopqrssss";
while ($string =~ /abc(def)ghi(jkl)mno(pqr)/isg) {
    print "  \n";
}

выход

$ perl perl.pl
def jkl pqr
def jkl pqr

У вас может быть три разных регулярных выражения, каждый из которых фокусируется на определенных группах. Очевидно, вы хотели бы просто назначить разные группы различным массивам в регулярном выражении,но я думаю, что ваш единственный вариант-разделить регулярное выражение.