Как я могу эффективно соответствовать многим различным regex шаблонам в Perl?

Я предполагаю много, зависит от того, что Вы подразумеваете под 'негосподствующей тенденцией'.

Был бы шепелявость количество как негосподствующая тенденция?

5
задан brian d foy 25 September 2009 в 23:31
поделиться

8 ответов

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

Увеличенный код из синопсиса модуля:

use Regexp::Assemble;

my $ra = Regexp::Assemble->new;
$ra->add( 'ab+c' );
$ra->add( 'ab+-' );
$ra->add( 'a\w\d+' );
$ra->add( 'a\d+' );
print $ra->re; # prints a(?:\w?\d+|b+[-c])

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

5
ответ дан 18 December 2019 в 06:51
поделиться

Вы можете комбинировать свои регулярные выражения с помощью оператора чередования | , например: / pattern1 | pattern2 | pattern3 /

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

  • Вы можете использовать модификатор регулярного выражения / x , чтобы аккуратно разделить их, по одному на строку. Предупреждение, если вы выберете это направление: вам придется явно указать ожидаемые пробелы, иначе они будут проигнорированы из-за / x .
  • Вы можете создать свой обычный выражение во время выполнения путем объединения отдельных источников. Примерно так (не проверено):

     my $ regex = join '|', @sources;
    while (<>) {
     следующий, если только / $ regex / o;
     сказать;
    }
    
5
ответ дан 18 December 2019 в 06:51
поделиться

Возможно, вы захотите избавиться от большого оператора if:

my @interesting = (
  qr/Failed in routing out/,
  qr/Agent .+ failed/,
  qr/Record Not Exist in DB/,
);

return unless $line =~ $_ for @interesting;

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

Может помочь, если вы сможете закрепить свои паттерны в начале, чтобы они могли быстрее выйти из строя.

4
ответ дан 18 December 2019 в 06:51
поделиться

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

Вот иллюстрация основной идеи :

use strict;
use warnings;

my @checks = (
    ['Failed',    qr/Failed in routing out/  ],
    ['failed',    qr/Agent .+ failed/        ],
    ['Not Exist', qr/Record Not Exist in DB/ ],
);
my @filter_strings = map { $_->[0] } @checks;
my @regexes        = map { $_->[1] } @checks;

sub regex {
    my $line = shift;
    for my $reg (@regexes){
        return 1 if $line =~ /$reg/;
    }
    return;
}

sub pre {
    my $line = shift;
    for my $fs (@filter_strings){
        return 1 if index($line, $fs) > -1;
    }
    return;
}

my @data = (
    qw(foo bar baz biz buz fubb),
    'Failed in routing out.....',
    'Agent FOO failed miserably',
    'McFly!!! Record Not Exist in DB',
);

use Benchmark qw(cmpthese);
cmpthese ( -1, {
    regex => sub { for (@data){ return $_ if(            regex($_)) } },
    pre   => sub { for (@data){ return $_ if(pre($_) and regex($_)) } },
} );

Вывод (результаты с вашими данными могут сильно отличаться):

             Rate     regex prefilter
regex     36815/s        --      -54%
prefilter 79331/s      115%        --
2
ответ дан 18 December 2019 в 06:51
поделиться

Это легко выполняется с помощью Perl 5.10

use strict;
use warnings;
use 5.10.1;

my @matches = (
  qr'Failed in routing out',
  qr'Agent .+ failed',
  qr'Record Not Exist in DB'
);

# ...

sub parse{
  my($filename) = @_;

  open my $file, '<', $filename;

  while( my $line = <$file> ){
    chomp $line;

    # you could use given/when
    given( $line ){
      when( @matches ){
        #...
      }
    }

    # or smartmatch
    if( $line ~~ @matches ){
      # ...
    }
  }
}

. Вы можете использовать новый оператор Smart-Match ~~ .

if( $line ~~ @matches ){ ... }

Или вы можете использовать ] с учетом / , когда . Это работает так же, как при использовании оператора Smart-Match.

given( $line ){
  when( @matches ){
    #...
  }
}
2
ответ дан 18 December 2019 в 06:51
поделиться

Может быть что-то вроде:

my @interesting = (
  qr/Failed in routing out/,
  qr/Agent .+ failed/,
  qr/Record Not Exist in DB/,
);

...


for my $re (@interesting) {
  if ($line =~ /$re/) {
    print $line;
    last;
  }
}

Вы можете попробовать объединить все свои паттерны с помощью "|" чтобы сделать одно регулярное выражение. Это может быть или не быть быстрее.

1
ответ дан 18 December 2019 в 06:51
поделиться

Компиляция выражения - очень трудоемкая операция, поэтому я сделаю это, только если вы планируете повторно использовать выражение. В противном случае я бы порекомендовал рефлексию; вы обнаружите, что он выполняется быстрее. Никогда не вызывайте выражение.Compile () в замкнутом цикле.

s ответ на Как эффективно сопоставить сразу несколько регулярных выражений?


Как эффективно сопоставить сразу несколько регулярных выражений?

(предоставлено brian d foy)

Не просите Perl скомпилировать регулярное выражение каждый раз, когда вы хотите сопоставить его. В этом примере perl должен перекомпилировать регулярное выражение для каждой итерации цикла foreach, так как он не знает, какой будет шаблон $.

@patterns = qw( foo bar baz );

LINE: while( <DATA> )
    {
    foreach $pattern ( @patterns )
        {
        if( /\b$pattern\b/i )
            {
            print;
            next LINE;
            }
        }
    }

Оператор qr // появился в perl 5.005. Он компилирует регулярное выражение, но не применяет его. Когда вы используете предварительно скомпилированную версию регулярного выражения, perl выполняет меньше работы. В этом примере я вставил карту, чтобы преобразовать каждый узор в его предварительно скомпилированную форму. Остальная часть скрипта такая же, но работает быстрее.

@patterns = map { qr/\b$_\b/i } qw( foo bar baz );

LINE: while( <> )
    {
    foreach $pattern ( @patterns )
        {
        if( /$pattern/ )
            {
            print;
            next LINE;
            }
        }
    }

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

$regex = join '|', qw( foo bar baz );

LINE: while( <> )
    {
    print if /\b(?:$regex)\b/i;
    }

Для получения дополнительных сведений об эффективности регулярных выражений см. Джеффри Фрейдл «Освоение регулярных выражений». Он объясняет, как работает механизм регулярных выражений и почему некоторые шаблоны на удивление неэффективны. Как только вы поймете, как Perl применяет регулярные выражения, вы сможете настроить их для индивидуальных ситуаций.

3
ответ дан 18 December 2019 в 06:51
поделиться

One possible solution is to let the regex state machine do the checking of alternatives for you. You'll have to benchmark to see if the result is noticeably more efficient, but it will certainly be more maintainable.

First, you'd maintain a file containing one pattern of interest per line.

Failed in routing out
Agent .+ failed
Record Not Exist in DB

Then you'd read in that file at the beginning of your run, and construct a large regular expression using the "alternative" operator, "|"

open(PATTERNS,"<foo.txt") or die $!;
@patterns = <PATTERNS>;
close PATTERNS or die $!;
chomp @patterns;
$matcher = join('|', @patterns);

while (<MYLOG>) {
    print if $_ =~ $matcher;
}
1
ответ дан 18 December 2019 в 06:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: