为什么^ A | bA慢于使用grep -P的负向lookbehind,但是使用grep -E快?

时间:2016-08-01 04:27:26

标签: regex grep pcre

这些是等价的:

grep -E '^A|bA'
grep -P '^A|bA'
grep -P '(?<![^b])A'

但是第二个grep -P '^A|bA'慢了好几倍。为什么呢?

他们都发现了同样的事情:开头Ab之后的一行。 (等效地,A之前没有b之外的其他行。)

第二行是否会禁用某些优化? grep当它认为更快时,会并行检查多个字符吗?我不能提出另一种解释,除非^|表示perl中的某些内容略有不同。

2 个答案:

答案 0 :(得分:5)

如果模式不包含反向引用,则GNU egrep(grep -E)使用DFA引擎 * ; grep -P使用了PCRE的NFA实施。 DFA引擎永远不会回溯,而模式^A|bA可以通过PCRE触发大量低效的回溯。

PCRE在字符串中的每个位置检查^A,然后检查bA,直到找到匹配为止。对于直到字符串末尾(或根本不匹配)不匹配的大输入,这可能需要很长时间。

您可以使用pcretest实用程序

查看此内容
$ pcretest
PCRE version 8.32 2012-11-30

  re> /^A|bA/C
data> bcAbcAbcA
--->bcAbcAbcA
 +0 ^             ^
 +1 ^             A
 +3 ^             b
 +4 ^^            A
 +0  ^            ^
 +3  ^            b
 +0   ^           ^
 +3   ^           b
 +0    ^          ^
 +3    ^          b
 +4    ^^         A
 +0     ^         ^
 +3     ^         b
 +0      ^        ^
 +3      ^        b
 +0       ^       ^
 +3       ^       b
 +4       ^^      A
 +0        ^      ^
 +3        ^      b
 +0         ^     ^
 +3         ^     b
No match

(?<![^b])A速度更快,因为PCRE不会在每个位置测试匹配,而是直接跳到第一个A;如果它不匹配,它将跳到下一个A,依此类推,直到字符串结束:

  re> /(?<![^b])A/C
data> bcAbcAbcA
--->bcAbcAbcA
 +0   ^           (?<![^b])
 +4   ^      ^    [^b]
 +8   ^           )
 +0      ^        (?<![^b])
 +4      ^   ^    [^b]
 +8      ^        )
 +0         ^     (?<![^b])
 +4         ^^    [^b]
 +8         ^     )
 +0          ^    (?<![^b])
 +4          ^    [^b]
 +8          ^    )
No match

有关DFA和NFA实施之间差异的详细信息,请参阅Russ Cox的文章&#34; Regular Expression Matching Can Be Simple And Fast&#34;。

*根据Jeffrey Friedl的掌握正则表达式第182页的"DFA Speed with NFA Capabilities: Regex Nirvana?"

答案 1 :(得分:-1)

使用-P选项时grep可能无法正常运行的原因部分是由于使用了不同的正则表达式(pcre)引擎,这在它使用的算法中更为复杂。快速浏览一下,了解发生了什么(GNU grep 2.20):

-E - 扩展的regexp

Starting program: /usr/bin/grep -Eq \^A\|bA wordlist.dic
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, 0x0000000000402ec0 in main ()
(gdb) si 200000
0x000000000040c667 in dfacomp ()
(gdb) bt
#0  0x000000000040c667 in dfacomp ()
#1  0x000000000040d618 in GEAcompile ()
#2  0x000000000040328a in main ()
(gdb) si 200000
0x00007ffff78410db in memchr () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff78410db in memchr () from /lib64/libc.so.6
#1  0x000000000040f952 in kwsexec ()
#2  0x000000000040dc2d in EGexecute ()
#3  0x000000000040500a in grepbuf ()
#4  0x0000000000405b60 in grepdesc ()
#5  0x000000000040330f in main ()
(gdb) si 200000
[Inferior 1 (process 23706) exited normally]

-P --perl-regexp

Starting program: /usr/bin/grep -Pq \^A\|bA wordlist.dic
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, 0x0000000000402ec0 in main ()
(gdb) si 200000
0x00007ffff7835eed in _int_malloc () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff7835eed in _int_malloc () from /lib64/libc.so.6
#1  0x00007ffff783826c in malloc () from /lib64/libc.so.6
#2  0x00007ffff7ba0fe4 in sljit_create_compiler () from /lib64/libpcre.so.1
#3  0x00007ffff7bbb32f in _pcre_jit_compile () from /lib64/libpcre.so.1
#4  0x00007ffff7bbfd8d in pcre_study () from /lib64/libpcre.so.1
#5  0x000000000041000e in Pcompile ()
#6  0x000000000040328a in main ()
(gdb) si 1200000
0x00007ffff7bbdc31 in _pcre_jit_exec () from /lib64/libpcre.so.1
(gdb) bt
#0  0x00007ffff7bbdc31 in _pcre_jit_exec () from /lib64/libpcre.so.1
#1  0x00007ffff7b9f083 in pcre_exec () from /lib64/libpcre.so.1
#2  0x0000000000410372 in Pexecute ()
#3  0x000000000040500a in grepbuf ()
#4  0x0000000000405b60 in grepdesc ()
#5  0x000000000040330f in main ()
... and still going ...

正如你所看到的那样,在调用pcre引擎而不是内置引擎时会有更多的事情发生。基本上在使用此正则表达式选项时,grep需要执行四次以上的指令才能搜索相同的模式。