代码之家  ›  专栏  ›  技术社区  ›  Jonathan Leffler

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

  •  38
  • Jonathan Leffler  · 技术社区  · 17 年前

    过去认为在Perl正则表达式的末尾包含“o”修饰符是有益的。电流 Perl documentation 甚至似乎都没有列出,当然也没有 modifiers section of perlre .

    信息技术 由于向后兼容的原因(如果没有其他原因),仍然接受。


    正如J A Faucett和brian d foy所指出的,“o”修饰语仍然被记录在案,如果你找到了正确的地方(其中一个不是 perlre perlop 页。它也可以在 perlreref 页。

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

    6 回复  |  直到 17 年前
        1
  •  38
  •   Alan Moore Chris Ballance    5 年前

    /o 不推荐使用。确保正则表达式只编译一次的最简单方法是使用正则表达式对象,如下所示:

    my $reg = qr/foo$bar/;
    

    插值 $bar $reg 初始化,并且从那时起将在封闭范围内使用缓存的、编译的正则表达式。但有时你 希望 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) {
          ...
        }
      }
    }
    

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

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

        2
  •  19
  •   brian d foy    11 年前

    /o 修饰符位于 perlop 文档,而不是 perlre 文档,因为它是一个类似引号的修饰符,而不是正则表达式修饰符。这对我来说总是很奇怪,但事实就是这样。自Perl 5.20以来,它现在列在 perlre

    在Perl5.6之前,即使变量没有更改,Perl也会重新编译正则表达式。你不需要再这样做了。你可以用 /o qr// 这样比较好。

        3
  •  6
  •   Rhubbarb    11 年前

    在Perl5版本20.0文档中 http://perldoc.perl.org/perlre.html 它说

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

    因此,最好避免这种选择。

        4
  •  4
  •   JacquesB    17 年前

        5
  •  2
  •   Peter Mortensen Pieter Jan Bonestroo    5 年前

    下面是调用匹配的不同方法的计时。

    $ 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
    }
    
        6
  •  0
  •   Never Sleep Again    11 年前

    令人困惑的是,它做了一件事

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

        7
  •  0
  •   Tim Potapov    5 年前

    是的,不是

    我使用以下脚本运行了一个简单的比较:

    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?