这些是等价的:
grep -E '^A|bA'
grep -P '^A|bA'
grep -P '(?<![^b])A'
但是第二个grep -P '^A|bA'
慢了好几倍。为什么呢?
他们都发现了同样的事情:开头A
或b
之后的一行。 (等效地,A
之前没有b
之外的其他行。)
第二行是否会禁用某些优化? grep
当它认为更快时,会并行检查多个字符吗?我不能提出另一种解释,除非^
或|
表示perl中的某些内容略有不同。
答案 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需要执行四次以上的指令才能搜索相同的模式。