为什么正则表达式/ [\ w \ W] + x / i运行起来会非常慢?

jm666

尝试:

time perl -E '$x="a" x 100000; $x =~ /[\w\W]+x/i'  

将运行很长时间(在我的笔记本上20秒)。没有/i,例如

time perl -E '$x="a" x 100000; $x =~ /[\w\W]+x/' 

在0.07秒内完成。

不管正则表达式[\w\W]没有多大意义,这种巨大的差异使我感到惊讶。

为什么会有如此大的差异?

编辑

更准确地说:

$ time perl -E '$x="a" x 100000; $x =~ /[\w\W]+x/i'  

real    0m19.479s
user    0m19.419s
sys 0m0.038s

我的 perl

Summary of my perl5 (revision 5 version 20 subversion 3) configuration:

  Platform:
    osname=darwin, osvers=15.0.0, archname=darwin-2level
    uname='darwin nox.local 15.0.0 darwin kernel version 15.0.0: sat sep 19 15:53:46 pdt 2015; root:xnu-3247.10.11~1release_x86_64 x86_64 '
    config_args='-Dprefix=/opt/anyenv/envs/plenv/versions/5.20.3 -de -Dusedevel -A'eval:scriptdir=/opt/anyenv/envs/plenv/versions/5.20.3/bin''
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=undef, usemultiplicity=undef
    use64bitint=define, use64bitall=define, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-fno-common -DPERL_DARWIN -fno-strict-aliasing -pipe -fstack-protector -I/opt/local/include',
    optimize='-O3',
    cppflags='-fno-common -DPERL_DARWIN -fno-strict-aliasing -pipe -fstack-protector -I/opt/local/include'
    ccversion='', gccversion='4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.1.76)', gccosandvers=''
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='env MACOSX_DEPLOYMENT_TARGET=10.3 cc', ldflags =' -fstack-protector -L/opt/local/lib'
    libpth=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/lib /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib /usr/lib /opt/local/lib
    libs=-lpthread -lgdbm -ldbm -ldl -lm -lutil -lc
    perllibs=-lpthread -ldl -lm -lutil -lc
    libc=, so=dylib, useshrplib=false, libperl=libperl.a
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
    cccdlflags=' ', lddlflags=' -bundle -undefined dynamic_lookup -L/opt/local/lib -fstack-protector'


Characteristics of this binary (from libperl): 
  Compile-time options: HAS_TIMES PERLIO_LAYERS PERL_DONT_CREATE_GVSV
                        PERL_HASH_FUNC_ONE_AT_A_TIME_HARD PERL_MALLOC_WRAP
                        PERL_NEW_COPY_ON_WRITE PERL_PRESERVE_IVUV
                        PERL_USE_DEVEL USE_64_BIT_ALL USE_64_BIT_INT
                        USE_LARGE_FILES USE_LOCALE USE_LOCALE_COLLATE
                        USE_LOCALE_CTYPE USE_LOCALE_NUMERIC USE_PERLIO
                        USE_PERL_ATOF
  Locally applied patches:
    Devel::PatchPerl 1.38
  Built under darwin
  Compiled at Oct 28 2015 14:46:19
  @INC:
    /opt/anyenv/envs/plenv/versions/5.20.3/lib/perl5/site_perl/5.20.3/darwin-2level
    /opt/anyenv/envs/plenv/versions/5.20.3/lib/perl5/site_perl/5.20.3
    /opt/anyenv/envs/plenv/versions/5.20.3/lib/perl5/5.20.3/darwin-2level
    /opt/anyenv/envs/plenv/versions/5.20.3/lib/perl5/5.20.3
    .

从问题的背景来看:实际代码将字符串与大量正则表达式(某种反垃圾邮件)匹配,因此我无法可靠地手动检查正则表达式数据库。真正的代码片段是

sub docheck {
    ...
    ...
    foreach my $regex (@$regexs) {
        if ( $_[0] =~ /$regex/i ) {

并且[\w\W]+是10k regexes之一:(,例如:[\w\W]+medicine\.netfirms\.com-regex-DB可能需要清理-但是...您知道:)

现在,代码已更改:

sub docheck {
    ...
    my $str = lc($_[0]);
    foreach my $regex (@$regexs) {
        if ( $str =~ /$regex/ ) {

所以避免/i

马里亚诺

TL; DR

在第二种情况下,优化器非常聪明,并且意识到"x"字符串中没有任何内容,因此不可能进行匹配,并且会更早失败。但是,对于这种/i情况,同时测试大写和小写都不太聪明x,因此它会继续尝试匹配整个正则表达式。


调试

尽管我无法重现如此大的性能差异,但是针对区分大小写的匹配触发了优化。

让我们在'debug'模式下运行它

代码

use re 'debug';
$x="a" x 100000;
$x =~ /[\w\W]+x/;
  • 您还可以添加-Mre=debug到perl调用中。

输出

Compiling REx "[\w\W]+x"
Final program:
   1: PLUS (13)
   2:   ANYOF[\x{00}-\x{7F}][{non-utf8-latin1-all}{unicode_all}] (0)
  13: EXACT <x> (15)
  15: END (0)
floating "x" at 1..9223372036854775807 (checking floating) stclass ANYOF[\x{00}-\x{7F}][{non-utf8-latin1-all}{unicode_all}] plus minlen 2 
Matching REx "[\w\W]+x" against "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"...
Intuit: trying to determine minimum start position...
  Did not find floating substr "x"...
Match rejected by optimizer
Freeing REx: "[\w\W]+x"

并注意最后一部分:

Intuit: trying to determine minimum start position...
  Did not find floating substr "x"...
Match rejected by optimizer

优化器尝试查找的第一个匹配项"x",由于找不到匹配项,因此它在正则表达式引擎甚至尝试匹配之前就拒绝匹配。

Perl正则表达式经过优化,希望尽早失败而不是成功。


慢速情况

我无法在终端上重现相同的行为(Perl v5.20.2),但在以后的优化中失败了,这可能是由于特定于版本的差异所致。但是,"x"在这种情况下不会进行不区分大小写的优化

不会触发此优化,因为它有超过一种匹配主题的可能性(小写"x"和大写"X")。

现在,在没有优化的情况下,正则表达式引擎理论上会尝试匹配"x"

  • 中的所有可能匹配项[\w\W]+(使用整个字符串,然后回溯1个字符,另一个...依此类推),以及
  • 主题字符串中的每个起始位置(99,999个位置)。

当然,还有其他优化措施可以减少此数量,但这就是为什么它这么慢的原因。请注意,它随着字符串的增加呈指数增长。

解决方法

如果您不特别要求x之前至少要有一个字符,则应使用/.*x/is,因为在第一个位置尝试匹配后,它会失败(优化器实际上会锚定.*到文本的开头)。
*感谢@nhahtdh提出了这一点。

但是,对于可能出现此行为的更一般的情况,可以选择一种提高性能的方法,"x"然后再进行检查(作为独立条件或在相同的正则表达式中):

$x =~ /(?:^(*COMMIT)(?=.*x))?[\w\W]+x/is;
  • (?:^...)?仅在字符串开头检查一次。
  • (?=.*x)如果有x未来
  • (*COMMIT)否则,它将回溯,并且COMMIT是一个使整个匹配失败的控制动词。

这将运行得更快。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

为什么此正则表达式匹配/ \ w + [^(] /?

来自分类Dev

为什么Java正则表达式中带有i修饰符的[\ W _] +匹配i,k,s?

来自分类Dev

PHP [\ w \-]正则表达式的含义是什么

来自分类Dev

正则表达式\ w匹配ê

来自分类Dev

为什么\ W元字符在此Javascript正则表达式中无法正常工作

来自分类Dev

正则表达式-/ \ w \ b \ w /

来自分类Dev

JavaScript中使用[\ w。] +而不是+的正则表达式

来自分类Dev

正则表达式匹配起始字符或_ \ w

来自分类Dev

w字正则表达式头痛

来自分类Dev

Perl正则表达式与\ w +不匹配

来自分类Dev

正则表达式说明[\ w- \。]

来自分类Dev

\ w的正则表达式匹配行为

来自分类Dev

正则表达式匹配起始字符或_ \ w

来自分类Dev

正则表达式使用\ w给出非字符

来自分类Dev

正则表达式\ w字符类和等号

来自分类Dev

python中的正则表达式(^ string \ s \ w +)

来自分类Dev

\W 用于检测转义序列 - 正则表达式

来自分类Dev

正则表达式 \w 不起作用

来自分类Dev

\W 匹配器 vs 空格?正则表达式

来自分类Dev

在ruby中,此正则表达式做什么?/(((w)\ 2 *)/

来自分类Dev

我的正则表达式(\ d。\ s.V13 \ d + \ sN \ d。\ s。\ w *)有什么问题?

来自分类Dev

正则表达式Java,为什么此正则表达式这么慢?

来自分类Dev

Python正则表达式-(\ w +)与复杂表达式一起使用时会产生不同的输出

来自分类Dev

Bash正则表达式-似乎无法匹配\ s \ S \ d \ D \ w \ W等中的任何一个

来自分类Dev

如何在Freemarker中使用正则表达式(\ b,\ w)?

来自分类Dev

正则表达式RightToLeft'\ w'匹配与默认值不同

来自分类Dev

正则表达式w / grep针对tnsnames.ora

来自分类Dev

如何解释此正则表达式/ [\ W _] / g

来自分类Dev

Javascript正则表达式仅允许\ w和特殊字符

Related 相关文章

  1. 1

    为什么此正则表达式匹配/ \ w + [^(] /?

  2. 2

    为什么Java正则表达式中带有i修饰符的[\ W _] +匹配i,k,s?

  3. 3

    PHP [\ w \-]正则表达式的含义是什么

  4. 4

    正则表达式\ w匹配ê

  5. 5

    为什么\ W元字符在此Javascript正则表达式中无法正常工作

  6. 6

    正则表达式-/ \ w \ b \ w /

  7. 7

    JavaScript中使用[\ w。] +而不是+的正则表达式

  8. 8

    正则表达式匹配起始字符或_ \ w

  9. 9

    w字正则表达式头痛

  10. 10

    Perl正则表达式与\ w +不匹配

  11. 11

    正则表达式说明[\ w- \。]

  12. 12

    \ w的正则表达式匹配行为

  13. 13

    正则表达式匹配起始字符或_ \ w

  14. 14

    正则表达式使用\ w给出非字符

  15. 15

    正则表达式\ w字符类和等号

  16. 16

    python中的正则表达式(^ string \ s \ w +)

  17. 17

    \W 用于检测转义序列 - 正则表达式

  18. 18

    正则表达式 \w 不起作用

  19. 19

    \W 匹配器 vs 空格?正则表达式

  20. 20

    在ruby中,此正则表达式做什么?/(((w)\ 2 *)/

  21. 21

    我的正则表达式(\ d。\ s.V13 \ d + \ sN \ d。\ s。\ w *)有什么问题?

  22. 22

    正则表达式Java,为什么此正则表达式这么慢?

  23. 23

    Python正则表达式-(\ w +)与复杂表达式一起使用时会产生不同的输出

  24. 24

    Bash正则表达式-似乎无法匹配\ s \ S \ d \ D \ w \ W等中的任何一个

  25. 25

    如何在Freemarker中使用正则表达式(\ b,\ w)?

  26. 26

    正则表达式RightToLeft'\ w'匹配与默认值不同

  27. 27

    正则表达式w / grep针对tnsnames.ora

  28. 28

    如何解释此正则表达式/ [\ W _] / g

  29. 29

    Javascript正则表达式仅允许\ w和特殊字符

热门标签

归档