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

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

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

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

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

person Incognito    schedule 14.02.2010    source источник
comment
Хотите подсчитать, сколько раз шаблон совпал? Вот так мне кажется...   -  person Zaid    schedule 14.02.2010
comment
мне нужно обработать совпадения   -  person Incognito    schedule 22.02.2010


Ответы (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;
}
person Leon Timmermans    schedule 14.02.2010
comment
это немного затянуто. при захвате вы можете использовать обратные ссылки - person ghostdog74; 14.02.2010
comment
ghostdog74: это дело вкуса. Если вы действительно назовете свои переменные $first и $second, то вы можете действительно использовать $1 и $2, но если вы дадите им более описательные имена, это может улучшить читаемость, чтобы сделать это так. - person Leon Timmermans; 14.02.2010
comment
-1. Я должен согласиться с ghostdog74; захват в серию переменных $1 .. просто чище в современном Perl. Хотя вы можете сделать это, это не значит, что это лучший способ сделать это. - person Robert P; 14.02.2010
comment
@leon Леон, правда, но, поскольку он все равно собирается поместить их в массивы, вас действительно волнует имя массива. кто не знает, что такое $1, $2 ..? - person ghostdog74; 14.02.2010
comment
Этот ответ, к сожалению, неверен. Цикл while в этом ответе будет бесконечным, если $string соответствует (из-за контекста списка внутри выражения while). - person YenForYang; 09.06.2021

Начиная с версии 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 и т. д., или если имена буферов не имеют значения, а имеет значение только порядок, вы можете использовать:

my @data;
if ( my @captured = $s =~ /abc (def) ghi (jkl) mno (pqr) /x ) {
    push @{ $data[$_] }, $captured[$_] for 0 .. $#captured;
}
person Sinan Ünür    schedule 14.02.2010
comment
Вы просто пытаетесь сделать глубокую копию в первом примере? Я бы просто вытащил dclone Storeable. Либо так, либо вашему примеру нужен цикл для создания значений, которые вы храните в $data. :) - person brian d foy; 22.02.2010
comment
@brian Я думал о том, чтобы проанализировать файл, в котором каждая строка дает вам значение first, second и third, и сохранить эти значения в своих собственных массивах. Сравните с примером Леона Тиммермана ( #2259795" title="как я могу хранить записи из регулярного выражения Perl в отдельные переменные"> stackoverflow.com/questions/2259784/ ) - person Sinan Ünür; 22.02.2010

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

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

# do something with it

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

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

person Robert P    schedule 14.02.2010

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

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

вывод

$ perl perl.pl
def jkl pqr
def jkl pqr
person ghostdog74    schedule 14.02.2010
comment
Обратите внимание на использование им модификатора g. Он выполняет глобальное совпадение, поэтому я предполагаю, что он хочет сохранить несколько совпадений. - person Leon Timmermans; 14.02.2010
comment
Кроме того, $1 и так далее — это не обратные ссылки, а захваты. Однако скобки и обратные ссылки связаны. - person jrockway; 14.02.2010

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

person joejoeson    schedule 14.02.2010