Perl正则表达式的'o'修饰符是否仍然提供任何好处?

时间:2009-02-15 03:30:23

标签: regex perl

过去认为在Perl正则表达式的末尾包含'o'修饰符是有益的。当前Perl documentation似乎甚至没有列出它,当然不是modifiers section of perlre

它现在提供任何好处吗?

仍然被接受,出于向后兼容的原因,如果没有其他原因。


如J A Faucett和brian d foy所述,如果您找到合适的地方(其中一个不是perlre文档),仍会记录'o'修饰符。在perlop页面中提到了它。它也可以在perlreref页面中找到。

正如Alan M在接受的答案中所指出的,更好的现代技术通常是使用qr //(引用的正则表达式)运算符。

7 个答案:

答案 0 :(得分:37)

我确信它仍然受到支持,但它已经过时了。如果你想只编译一次正则表达式,你最好使用正则表达式对象,如下所示:

my $reg = qr/foo$bar/;

$bar的插值是在初始化变量时完成的,因此您将始终在封闭范围内使用缓存的,已编译的正则表达式。但有时你想要重新编译正则表达式,因为你希望它使用变量的新值。以下是Friedl在The Book中使用的示例:

sub CheckLogfileForToday()
{
  my $today = (qw<Sun Mon Tue Wed Thu Fri Sat>)[(localtime)[6]];

  my $today_regex = qr/^$today:/i; # compiles once per function call

  while (<LOGFILE>) {
    if ($_ =~ $today_regex) {
      ...
    }
  }
}

在函数范围内,$ today_regex的值保持不变。但是下次调用该函数时,将使用新值$today重新编译正则表达式。如果他刚刚使用

if ($_ =~ m/^$today:/io)

......正则表达式永远不会更新。因此,使用对象形式,您可以在不牺牲灵活性的情况下获得/ o的效率。

答案 1 :(得分:17)

/o修饰符位于perlop文档中,而不是perlre文档中,因为它是类似引号的修饰符而不是正则表达式修饰符。这对我来说似乎总是很奇怪,但事实就是如此。从Perl 5.20开始,它现在列在perlre中只是为了注意你可能不应该使用它。

在Perl 5.6之前,即使变量没有改变,Perl也会重新编译正则表达式。你不需要那样做了。尽管对变量进行了进一步更改,您仍可以使用/o编译正则表达式,但正如其他答案所指出的那样,qr//对此更好。

答案 2 :(得分:6)

在Perl 5版本20.0文档中 http://perldoc.perl.org/perlre.html 它说明了

Modifiers

Other Modifiers

…

o - pretend to optimize your code, but actually introduce bugs

这可能是一种幽默的说法,它应该执行某种优化,但实施却被打破了。

因此,最好避免使用该选项。

答案 3 :(得分:4)

这是正则表达式包含变量引用的情况下的优化。它表明正则表达式即使在其中有变量也不会改变。这允许以其他方式无法实现的优化。

答案 4 :(得分:2)

以下是调用匹配方式的不同方法。

$ perl -v | grep version
This is perl 5, version 20, subversion 1 (v5.20.1) built for x86_64-linux-gnu-thread-multi

$ perl const-in-re-once.pl | sort
0.200   =~ CONST
0.200   =~ m/$VAR/o
0.204   =~ m/literal-wo-vars/
0.252   =~ m,@{[ CONST ]},o
0.260   =~ $VAR
0.276   =~ m/$VAR/
0.336   =~ m,@{[ CONST ]},

我的代码:

#! /usr/bin/env perl

use strict;
use warnings;

use Time::HiRes qw/ tv_interval clock_gettime gettimeofday /;
use BSD::Resource qw/ getrusage RUSAGE_SELF /;

use constant RE =>
    qr{
        https?://
        (?:[^.]+-d-[^.]+\.)?
        (?:(?: (?:dev-)? nind[^.]* | mr02 )\.)?
        (?:(?:pda|m)\.)?
        (?:(?:news|haber)\.)
        (?:.+\.)?
        yandex\.
        .+
    }x;

use constant FINAL_RE => qr,^@{[ RE ]}(/|$),;

my $RE = RE;

use constant ITER_COUNT => 1e5;

use constant URL => 'http://news.trofimenkov.nerpa.yandex.ru/yandsearch?cl4url=www.forbes.ru%2Fnews%2F276745-visa-otklyuchila-rossiiskie-banki-v-krymu&lr=213&lang=ru';

timeit(
    '=~ m/literal-wo-vars/',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m{
                ^https?://
                (?:[^.]+-d-[^.]+\.)?
                (?:(?: (?:dev-)? nind[^.]* | mr02 )\.)?
                (?:(?:pda|m)\.)?
                (?:(?:news|haber)\.)
                (?:.+\.)?
                yandex\.
                .+
                (/|$)
            }x
        }
    }
);

timeit(
    '=~ m/$VAR/',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^$RE(/|$),
        }
    }
);

timeit(
    '=~ $VAR',
    ITER_COUNT,
    sub {
        my $r = qr,^$RE(/|$),o;
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ $r
        }
    }
);

timeit(
    '=~ m/$VAR/o',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^$RE(/|$),o
        }
    }
);

timeit(
    '=~ m,@{[ CONST ]},',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^@{[ RE ]}(/|$),
        }
    }
);

timeit(
    '=~ m,@{[ CONST ]},o',
    ITER_COUNT,
    sub {
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ m,^@{[ RE ]}(/|$),o
        }
    }
);

timeit(
    '=~ CONST',
    ITER_COUNT,
    sub {
        my $r = qr,^$RE(/|$),o;
        for (my $i = 0; $i < ITER_COUNT; ++$i) {
            URL =~ FINAL_RE
        }
    }
);

sub timeit {
    my ($name, $iters, $code) = @_;
    #my $t0 = [gettimeofday];
    my $t0 = (getrusage RUSAGE_SELF)[0];
    $code->();
    #my $el = tv_interval($t0);
    my $el = (getrusage RUSAGE_SELF)[0] - $t0;
    printf "%.3f\t%-17s\t%.9f\n", $el, $name, $el / $iters
}

答案 5 :(得分:0)

奇怪的是,做的一件事是,允许一个ONCE块,至少是5.8.8。

perl -le 'for (1..3){ print; m/${\(print( "between 1 and 2 only"), 3)}/o and print "matched" }'

答案 6 :(得分:0)

是和否

我使用以下脚本进行了简单的比较:

perl -MBenchmark=cmpthese -E 'my @n = 1..10000; cmpthese(10000, {string => sub{"a1b" =~ /a\d+c/ for @n}, o_flag => sub{"a1b" =~ /a\d+c/o for @n}, qr => sub{my $qr = qr/a\d+c/; "a1b" =~ /$qr/ for @n } })'

结果如下:

         Rate     qr string o_flag
qr      760/s     --   -72%   -73%
string 2703/s   256%     --    -5%
o_flag 2833/s   273%     5%     --

所以,/o 标志显然比使用 qr

但显然 /o 标志可能会导致错误Perl regex /o optimization or bug?