Как я могу определить, какая из альтернатив соответствует шаблону регулярного выражения Perl? - PullRequest
3 голосов
/ 09 ноября 2011

У меня есть список регулярных выражений (около 10 - 15), которые мне нужно было сопоставить с каким-либо текстом.Сопоставлять их по одному в цикле слишком медленно.Но вместо того, чтобы написать свой собственный конечный автомат для сопоставления всем регулярным выражениям одновременно, я пытаюсь | отдельных регулярных выражений и позволить Perl делать свою работу.Проблема в том, как мне узнать, какая из альтернатив соответствует?

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

Итак, со следующим:

/^(A(\d+))|(B(\d+))|(C(\d+))$/

и строкой", как я могу узнать, что A123 соответствует и извлечь" 123 "?

Ответы [ 5 ]

5 голосов
/ 09 ноября 2011

Почему бы не использовать /^ (?<prefix> A|B|C) (?<digits> \d+) $/x.Обратите внимание, что именованные группы захвата используются для ясности и не обязательны.

5 голосов
/ 09 ноября 2011

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

2 голосов
/ 09 ноября 2011

A123 будет в группе захвата $1, а 123 будет в группе $2

Таким образом, вы можете сказать:

if ( /^(A(\d+))|(B(\d+))|(C(\d+))$/ && $1 eq 'A123' && $2 eq '123' ) {
    ...
}

Это избыточно, ноу вас есть идея ...

РЕДАКТИРОВАТЬ: Нет, вам не нужно перечислять каждый под матч, вы спросили, как узнать, соответствует ли A123 и как извлечь 123:

  • Вы не войдете в блок if, если A123 не соответствует
  • , и вы не можете извлечь 123, используя обратную ссылку $2.

Так что, возможно, этот пример был бы более понятным:

if ( /^(A(\d+))|(B(\d+))|(C(\d+))$/ ) {
    # do something with $2, which will be '123' assuming $_ matches /^A123/
}

РЕДАКТИРОВАТЬ 2:

Для захвата матчей в AoA (который отличаетсявопрос, но это должно быть сделано):

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @matches = map { [$1,$2] if /^(?:(A|B|C)(\d+))$/ } <DATA>;
print Dumper \@matches;

__DATA__
A123
B456
C769

Результат:

$VAR1 = [
          [
            'A',
            '123'
          ],
          [
            'B',
            '456'
          ],
          [
            'C',
            '769'
          ]
        ];

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

1 голос
/ 10 ноября 2011

С вашими примерами легко написать

'A123' =~ /^([ABC])(\d+)$/;

, после чего $ 1 будет содержать префикс, а $ 2 - суффикс.

Я не могу сказать, относится ли это к вашим реальным данным, но использование дополнительного модуля кажется излишним.

0 голосов
/ 11 марта 2016

Еще одна вещь, которую вы можете сделать в Perl, это встроить код Perl непосредственно в регулярное выражение, используя "(? {...})".Таким образом, вы можете установить переменную, которая сообщит вам, какая часть регулярного выражения соответствует.ВНИМАНИЕ: ваше регулярное выражение не должно содержать никаких переменных (кроме встроенного кода Perl), которые будут интерполированы в регулярное выражение, иначе вы получите ошибки.Вот пример синтаксического анализатора, который использует эту функцию:

my $kind;
my $REGEX  = qr/
          [A-Za-z][\w]*                        (?{$kind = 'IDENT';})
        | (?: ==? | != | <=? | >=? )           (?{$kind = 'OP';})
        | -?\d+                                (?{$kind = 'INT';})
        | \x27 ( (?:[^\x27] | \x27{2})* ) \x27 (?{$kind = 'STRING';})
        | \S                                   (?{$kind = 'OTHER';})
        /xs;

my $line = "if (x == 'that') then x = -23 and y = 'say ''hi'' for me';";
my @tokens;
while ($line =~ /( $REGEX )/xsg) {
    my($match, $str) = ($1,$2);
    if ($kind eq 'STRING') {
        $str =~ s/\x27\x27/\x27/g;
        push(@tokens, ['STRING', $str]);
        }
    else {
        push(@tokens, [$kind, $match]);
        }
    }
foreach my $lItems (@tokens) {
    print("$lItems->[0]: $lItems->[1]\n");
    }

, который печатает следующее:

IDENT: if
OTHER: (
IDENT: x
OP: ==
STRING: that
OTHER: )
IDENT: then
IDENT: x
OP: =
INT: -23
IDENT: and
IDENT: y
OP: =
STRING: say 'hi' for me
OTHER: ;

Это немного надумано, но вы заметите, что кавычкиапострофы) вокруг строк удаляются (кроме того, последовательные кавычки свернуты в одинарные кавычки), поэтому, как правило, только переменная $ kind сообщит вам, видел ли анализатор идентификатор или строку в кавычках.

...