Регулярное выражение Perl: как узнать количество совпадений

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

for my $regex (@{$regexs_ref}) {
    LINE: for (@rawfile) {
        /@$regex/ && do {
            # do something here
            next LINE;
        };
    }
}

Есть ли способ узнать, сколько совпадений у меня есть (чтобы я мог обработать их соответствующим образом ..)?

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


person Lenny Benny    schedule 31.08.2010    source источник
comment
Что вы имеете в виду, когда говорите, сколько у вас спичек? Сколько раз определенный шаблон совпадал в файле? Какой-то подшаблон в заданном выражении? Что вы подразумеваете под соответствующей обработкой? Наши предложения будут более полезными для вас, если вы предоставите больше контекста.   -  person Greg Bacon    schedule 31.08.2010
comment
В основном я спрашиваю, можно ли было это написать лучше? Единственное изменение состоит в том, что теперь у меня есть регулярные выражения в базе данных. code.google.com/p/codalyzer/source/ просматривать/багажник/парсер/   -  person Lenny Benny    schedule 31.08.2010
comment
Вы читали perldoc perlop (особенно разделы s/// и m//)?   -  person Ether    schedule 31.08.2010
comment
@ Эфир, да, почему ты спрашиваешь?   -  person Lenny Benny    schedule 31.08.2010
comment
@Karl: потому что ответ там. :)   -  person Ether    schedule 31.08.2010
comment
Можно немного конкретнее? знак равно   -  person Lenny Benny    schedule 31.08.2010
comment
Думаю, теперь я понимаю, что вы имеете в виду.. :-) Спасибо!   -  person Lenny Benny    schedule 31.08.2010


Ответы (4)


Если вы выполняете сопоставление в контексте списка (т. е., по сути, присваиваете списку), вы получаете все свои совпадения и группировки в списке. Затем вы можете просто использовать этот список в скалярном контексте, чтобы получить количество совпадений.

Или я неправильно понимаю вопрос?

Пример:

my @list = /$my_regex/g;
if (@list)
{
  # do stuff
  print "Number of matches: " . scalar @list . "\n";
}
person Platinum Azure    schedule 31.08.2010
comment
Хм, возможно то, что я ищу. @list теперь содержит все совпадения (например, $1, $2, $3..) или..? - person Lenny Benny; 31.08.2010
comment
Да, у @list есть все совпадения. (И, конечно же, размер списка — это количество совпадений!) На самом деле, если вы собираетесь что-то делать с каждым захватом/совпадением, разумнее всего просто перебирать этот список совпадений с помощью foreach, примерно так, как C-стиль for не рекомендуется в Perl. :-) - person Platinum Azure; 31.08.2010
comment
Отлично. Могу ли я использовать свой array_ref в качестве @list? Можете ли вы показать мне пример? Да, я собираюсь что-то делать с каждым захватом/сопоставлением, как в code.google.com/p/codalyzer/source/browse/trunk/parser/ - person Lenny Benny; 31.08.2010
comment
... только теперь я храню все регулярные выражения в базе данных и искал лучший способ сделать это. :-) - person Lenny Benny; 31.08.2010
comment
@Karl S Haugen: Не могли бы вы пояснить, что вы имеете в виду? (я не вижу array_ref в приведенном выше коде) - person Platinum Azure; 31.08.2010
comment
Я думал примерно так: мои @хиты; для моего $regex (@{$regexs_ref}) { LINE: for (@rawfile) { @hits = ($_ =~ /@$regex/); } } Конечно, я могу заменить for на foreach :-). Это то, о чем вы думали? - person Lenny Benny; 31.08.2010
comment
Возможно, мне нужно переосмыслить это. Может быть, просто явно ввести каждое регулярное выражение - лучший способ ..? Как сделать в ссылке выше. В основном, что я хочу сделать: проанализировать файл журнала, сделать что-то с совпадениями, а затем вставить соответственно в базу данных. Лучшее решение?? - person Lenny Benny; 31.08.2010
comment
Да, в принципе должно работать. Я понимаю часть @{$regexs_ref}, но почему у вас есть /@$regex/ в качестве шаблона соответствия? Вы хотите сопоставить буквальный символ @ перед вашим регулярным выражением? Если это так, вы можете избежать знака @, иначе Perl может интерпретировать это как разыменование массива. (Вам не нужны скобки для простого ссылочного скаляра, поэтому Perl может запутаться в регулярном выражении) - person Platinum Azure; 31.08.2010
comment
Разве это не ссылка на разыменованный массив, которую я получаю от fetchall_arrayref()? У меня вроде $regexs_ref = $sth-›fetchall_arrayref(); - person Lenny Benny; 31.08.2010
comment
stackoverflow.com/questions/3612756/ - person Lenny Benny; 01.09.2010
comment
Да, я получаю часть ссылки на массив (для $regexs_ref. Это нормально. Это /@$regex/, которого я не получаю. Шаблон регулярного выражения представляет собой строку и, следовательно, должен быть скалярным. По сути, поскольку вы выполняете два шага разыменования, это похоже, что вы ожидаете, что $regexs_ref будет содержать список из ‹b›дополнительных ссылок на списки‹/b›, которые, в свою очередь, ссылаются на списки. На самом деле у вас есть только ссылка на один список шаблонов регулярных выражений, если я понимаю это правильно Я прав? - person Platinum Azure; 01.09.2010

Вам нужно будет следить за этим самостоятельно. Вот один из способов сделать это:

#!/usr/bin/perl

use strict;
use warnings;

my @regexes = (
    qr/b/,
    qr/a/,
    qr/foo/,
    qr/quux/,
);

my %matches = map { $_ => 0 } @regexes;
while (my $line = <DATA>) {
    for my $regex (@regexes) {
        next unless $line =~ /$regex/;
        $matches{$regex}++;
    }
}

for my $regex (@regexes) {
    print "$regex matched $matches{$regex} times\n";
}

__DATA__
foo
bar
baz
person Chas. Owens    schedule 31.08.2010

При обработке CA::Parser связанные с совпадениями для /$CA::Regex::Parser{Kills}{all}/, вы используете захваты $1 на протяжении всего $10, а большинство остальных используют меньше. Если под количеством совпадений вы подразумеваете количество захватов (наибольшее значение n, для которого $n имеет значение), вы можете использовать специальный массив Perl @- (выделение добавлено):

@LAST_MATCH_START

@-

$-[0] — это смещение начала последнего успешного совпадения. $-[n] — это смещение начала подстроки, совпавшей с n-м подшаблоном, или undef, если подшаблон не совпал. Таким образом, после матча с $_ $& совпадает с substr $_, $-[0], $+[0] - $-[0]. Точно так же $n совпадает с

substr $_, $-[n], $+[n] - $-[n]

если $-[n] определено, а $+ совпадает с

substr $_, $-[$#-], $+[$#-] - $-[$#-]

Можно использовать $#-, чтобы найти последнюю совпавшую подгруппу в последнем успешном совпадении. В отличие от $#+, количества подгрупп в регулярном выражении. Сравните с @+.

Этот массив содержит смещения начала последних успешных подсовпадений в текущей активной динамической области. $-[0] — это смещение в строке начала всего совпадения. n-й элемент этого массива содержит смещение n-го подсовпадения, поэтому $-[1] — это смещение, где начинается $1, $-[2] — смещение, где начинается $2, и так далее.

После совпадения с некоторой переменной $var:

  • $` совпадает с substr($var, 0, $-[0])
  • $& совпадает с substr($var, $-[0], $+[0] - $-[0])
  • $' совпадает с substr($var, $+[0])
  • $1 совпадает с substr($var, $-[1], $+[1] - $-[1])
  • $2 совпадает с substr($var, $-[2], $+[2] - $-[2])
  • $3 совпадает с substr($var, $-[3], $+[3] - $-[3])

Пример использования:

#! /usr/bin/perl

use warnings;
use strict;

my @patterns = (
  qr/(foo(bar(baz)))/,
  qr/(quux)/,
);

chomp(my @rawfile = <DATA>);

foreach my $pattern (@patterns) {
  LINE: for (@rawfile) {
    /$pattern/ && do {
      my $captures = $#-;
      my $s = $captures == 1 ? "" : "s";
      print "$_: got $captures capture$s\n"; 
    };
  }
}

__DATA__
quux quux quux
foobarbaz

Вывод:

foobarbaz: got 3 captures
quux quux quux: got 1 capture
person Greg Bacon    schedule 31.08.2010
comment
Спасибо, это мне очень помогло. знак равно - person Lenny Benny; 31.08.2010

Как насчет кода ниже:

my $string = "12345yx67hjui89";    
my $count = () = $string =~ /\d/g;
print "$count\n";

Он печатает 9 здесь, как и ожидалось.

person Jassi    schedule 08.09.2014
comment
Это не что иное, как слияние двух типичных линий в одну. Вы можете сделать то же самое с двумя линиями. мой @count = $string =~ /\d/g; вывести скаляр @count; Итак, зачем хранить совпадающие значения в массиве, если это не требуется, вместо этого просто используйте пустой список и присвойте его скаляру. Это будет сделать. - person Jassi; 09.09.2014