簡體   English   中英

如何在Perl中有效地匹配許多不同的正則表達式模式?

[英]How can I efficiently match many different regex patterns in Perl?

我有越來越多的正則表達式,我用它來解析搜索“有趣”錯誤和調試語句的日志文件。 我現在將它們分成5個桶,其中大部分落入3個大桶中。 到目前為止,我有超過140種模式,而且這個模式正在不斷增長。

大多數正則表達式都很簡單,但它們也相當獨特,因此我用單一模式捕獲多個匹配的機會很少。 由於我匹配的性質,模式往往是模糊的,因此很少匹配,所以我在每個輸入行上做一個TON工作,最終結果是它無法匹配任何東西或匹配最后的通用之一。

由於輸入的數量(數百兆字節的日志文件),我有時會等待一兩分鍾才能完成腳本。 因此,我希望有一個更有效的解決方案。 不過,我對犧牲速度的清晰度並不感興趣。

我目前正常的表達式設置如下:

 if (($line =~ m{Failed in routing out}) ||
  ($line =~ m{Agent .+ failed}) ||
  ($line =~ m{Record Not Exist in DB}) ||
         ...

是否有更好的方法來構建它,以便它更有效,但仍然可維護? 謝謝!

您可以使用交替操作結合你的正則表達式| ,如: /pattern1|pattern2|pattern3/

顯然,如果將所有這些都放在一行中,它將無法維護,但您可以選擇減輕這種情況。

  • 您可以使用/x regex修改器很好地隔開它們,每行一個。 如果你選擇這個方向,請注意:你必須明確指定你期望的空格字符,否則它們會因為/x而被忽略。
  • 您可以通過組合各個源在運行時生成正則表達式。 像這樣(未經測試):

     my $regex = join '|', @sources; while (<>) { next unless /$regex/o; say; } 

您可能想看一下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])

您甚至可以從單獨的文件中剔除正則表達式集合。

您可能想要擺脫大的if語句:

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

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

雖然我不能保證這會改善任何不用真實數據進行基准測試的東西。

如果您可以在開始時錨定模式以便它們可以更快地失敗,這可能會有所幫助。

perlfaq6的答案我如何有效地匹配多個正則表達式?


如何有效地匹配多個正則表達式?

(由brian d foy提供)

每次要匹配時,都不要讓Perl編譯正則表達式。 在這個例子中,perl必須為foreach循環的每次迭代重新編譯正則表達式,因為它無法知道$ pattern是什么。

@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;
    }

有關正則表達式效率的更多詳細信息,請參閱Jeffrey Freidl掌握正則表達式。 他解釋了正則表達式引擎如何工作以及為什么某些模式效率低得驚人。 一旦了解了perl如何應用正則表達式,您就可以針對各種情況調整它們。

您的示例正則表達式看起來主要基於普通單詞和短語。 如果是這種情況,您可以通過使用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%        --

這可以通過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 ){ ... }

或者您可以使用given / when 其功能與使用Smart-Match運算符相同。

given( $line ){
  when( @matches ){
    #...
  }
}

一種可能的解決方案是讓正則表達式狀態機為您檢查替代方案。 你必須進行基准測試,看看結果是否明顯更有效,但它肯定會更易於維護。

首先,您將維護一個包含每行感興趣模式的文件。

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

然后你在運行開始時讀取該文件,並使用“alternative”運算符“ | ”構造一個大的正則表達式|

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

while (<MYLOG>) {
    print if $_ =~ $matcher;
}

也許是這樣的:

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;
  }
}

您可以嘗試使用“|”加入所有模式 做一個正則表達式。 這可能會也可能不會更快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM