哪种正则表达式更高效?

5
我正在解析一些巨大的日志文件,并且有一些非常简单的字符串匹配,例如:
if(m/Some String Pattern/o){
    #Do something
}

看起来很简单,但实际上我大部分的匹配可能是针对行首的,但实际匹配会更“长”,例如:

if(m/^Initial static string that matches Some String Pattern/o){
    #Do something
}

显然,这是一个较长的正则表达式,因此匹配需要更多的工作。不过,我可以使用行首锚点,以便更早地将表达式丢弃为匹配失败。

我猜后者会更有效率。有人能支持或反驳我的想法吗?:-)


如果你要锚定到一行的开头,需要添加多行修饰符:m/^anchored regex/m - Alan Moore
1
请在这里查看我的基准测试帖子。你的直觉是不正确的... - dawg
7个回答

6
我认为您会发现,使用^作为正则表达式的开头肯定更快,因为正则表达式引擎不必寻找字符串中的任何匹配项,只需要查找字符串的左边缘即可。
当然,这是一件很容易测试和测量的事情。进行大约1000万次正则表达式匹配,测量所需时间,然后再尝试使用不同的正则表达式。

4

使用行锚点可以提高速度。但需要说明的是,这里并不需要//o修饰符,实际上它起不到任何作用。在我看来,这是代码气味。

以前存在使用//o的情况,但现在由qr//提供相同功能。


3

一个正则表达式的速度取决于两个因素:正则表达式本身和传递给正则表达式的数据。通常来说,没有回溯的锚定正则表达式(起始或结束)会比其他正则表达式更快。但是如果你在处理每一行都为空的文件时,/^hello//hello/ 之间没有速度差异(至少如果正则表达式引擎编写正确的话)。

但我的原则是:要测量,而不是猜测。


3

我按照建议进行了一些计时。这是我的应用程序的结果。它是整个应用程序,而不仅仅是正则表达式搜索。它扫描了60000行。11个正则表达式的平均长度约为30个字符。长但锚定的正则表达式大约有120个字符。

Short
   real    0m58.780s
   user    0m54.940s
   sys     0m0.790s

Long (anchored)
   real    0m54.260s
   user    0m53.630s
   sys     0m0.490s

Long (not anchored)
   real    0m54.705s
   user    0m54.130s
   sys     0m0.400s

因此,锚定长字符串的速度略微更快。虽然差别不大。如果我的字符串更大,情况可能会有所不同。


Long(锚定)和Long(未锚定)的时间差异足够小,以至于可以落在基准测试的噪声范围内——除非您在单用户模式下运行且没有其他进程运行。负责任的结论是这没有任何区别。 - Schwern

2
你可以通过使用 use re debug 命令,详细了解Perl中正则表达式引擎的运行情况。这个命令的文档可以在这里找到。
阅读Perl建议的性能优化技巧以及建议的计时方法,总是很有帮助的。
如果我运行这个小测试:
#!/usr/bin/perl 

use strict;
use warnings;
use Benchmark;

my $target="aeiou";

my $str="lkdjflzdjfljdsflkjasdjf asldkfj lasdjf dslfj sldfj asld alskdfj lasd f";

my $str2=$str.$target;

timethese(10_000_000, {
            'float'       => sub {
                die "no match" unless $str2=~m/$target/o;
            },
            'anchored'  => sub {
                die "no match" unless $str2=~m/^.*$target/o;
            },
            'prefixed'   => sub {
                die "no match" unless $str2=~m/^$str$target/o ;
            },  

    });

我得到了以下输出:
Benchmark: timing 10000000 iterations of anchored, float, prefixed...
  anchored:  4 wallclock secs ( 3.46 usr +  0.01 sys =  3.47 CPU) @ 2881844.38/s 
     float:  2 wallclock secs ( 1.87 usr +  0.00 sys =  1.87 CPU) @ 5347593.58/s 
  prefixed:  4 wallclock secs ( 3.05 usr +  0.01 sys =  3.06 CPU) @ 3267973.86/s 

这表明未锚定(浮动)版本要快得多。但是,正则表达式和源代码可能会改变这一点。因此,您需要自己测试以确定最佳方案。


你的基准测试表明 /$target//^.*$target/ 更快,这是我直觉上预期的(但测量总是一个好主意)。原帖中锚定的字面字符串没有通配符,可能会显示出与您测量的性能特征不同的性能特征。 - Greg Hewgill
但是看看“prefixed”,这正是OP所问的:1)锚定字符串,2)以字符串字面量为前缀,3)目标在某个字符串字面量中,4)没有通配符。因此,锚定的“prefixed”版本与通配符版本一样慢。浮动字符串比锚定前缀字符串(OP)和锚定通配符更快。 - dawg

1

你是说你可以通过添加静态前缀来锚定正则表达式,就像这样吗?

/^blah blah The Real Regex/

这肯定不会影响性能,而且可能会有所帮助,但不是出于你想的原因。尽管正则表达式引擎以锚点、环视和捕获组等“神奇”功能而闻名,但它们最擅长的是匹配字符字面序列。序列越长,匹配速度就越快(当然,在一定程度上)。

换句话说,是静态前缀的添加,而不是锚点,为你提供了帮助。


0

我投票支持你所说的那个以开头为锚点的选项!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接