3

我正在尝试优化将在特定目录树中的每个文件上运行正则表达式的脚本。的所有组件都在正常工作,但我正试图让正则表达式尽可能快地运行。

该脚本在每个文件上运行许多正则表达式。我们正在尝试做这样的事情:

我们从一个 YAML 文件开始:

---
-
  description: has foo
  regex: foo
-
  description: has bar
  regex: bar
-
  description: has foofoo
  regex: foofoo
-
  description: has barbar
  regex: barbar

然后我们将文件读入一个数组(并通过 qr// 运行正则表达式字符串来编译它们),如下所示:

my @regex = @{LoadFile('yaml_file')};
foreach ( @regex ) { $_->{'regex'} = qr/$_->{'regex'}/ } 

然后像这样评估每个文件上的每个正则表达式

foreach my $r ( @regex ) {
    if ( $slurped_file_text =~ /$r->{'regex'}/ ){
        stuff;
    }
}

我们发现,上述方法比像这样扩展 if/elsif 语句要慢得多:

if( $slurped_file_text =~ /foo/ ){
    stuff;
}elsif( $slurped_file_text =~ /bar/ ){
    stuff;
}elsif( $slurped_file_text =~ /foofoo/ ){
    stuff;
}elsif( $slurped_file_text =~ /barbar/ ){
    stuff;
}

但是,这个 if/elsif 方法不是 DRY,我们需要能够轻松地将正则表达式添加到我们的列表中,而不必每次都编辑脚本代码。

在查看了 NYTProf 的 foreach 做事方式后,发现调用 main::CORE::regcomp 花费了大量时间。

在阅读了类似的问题后,我们发现了 o 运算符,它应该表示正则表达式自编译以来没有更改,因此不需要重新编译。所以然后我们尝试了这个(基本上只是将 o 添加到顶部代码):

foreach my $r ( @regex ) {
    if ( $slurped_file_text =~ /$r->{'regex'}/o ){
        stuff;
    }
}

这给了我们想要的速度,但它没有正确评估正则表达式。当匹配模式存在时,它不会返回 true。

我知道 o 运算符不再大量使用,但如上所述,我们仍在使用 perl v5.10.1,并且此版本的文档表明我们需要 o 运算符才能获得我们正在寻找的性能。

我的问题是:

  • 我们如何使用 o 运算符正确评估正则表达式?或者您对 o 运算符有什么了解可以解释这里发生的事情吗?
  • 您是否看到在一组文件上运行动态正则表达式列表的更有效方法。

非常感谢任何和所有帮助。

4

1 回答 1

5

循环版本比展开循环的版本慢的原因是因为始终检查循环版本中的每个正则表达式,但您只检查直到在展开版本中找到匹配项。

my @regexs = map $_->{regex}, @{ LoadFile('yaml_file') };

for my $text (@texts) {
   for my $regex (@regexs) {
      if ($text =~ $regex) {
         stuff;
         last;       <---- Missing
      }
   }
}

但是由于您似乎并不关心匹配哪种模式,您应该只构建一个模式并编译它。

my $pattern = join '|', map "(?:$_->{regex})", @{ LoadFile('yaml_file') };
my $regex = qr/$pattern/;

for my $text (@texts) {
   if ($text =~ $regex) {
      stuff;
   }
}
于 2013-11-14T03:35:53.073 回答