首先明确几点概念:
1。断言是一个对当前匹配位置之前或之后的字符的测试;
2。断言不会实际消耗任何字符; #这一点至关重要
通常的用法: (?<=a)b(?=c) #b的前瞻是c,后瞻是a;
前瞻左置用法:(?=\d)\w+ #\w+需要以\d开始;
后瞻右置用法:\w+(?<=\d) #\w+需要以\d结束;
上面三种用法中,我们姑且把b和\w+称为修饰项。
对于后两种用法,要求 前瞻/后瞻 里用以约束修饰项的字符应当是修饰项的子集,否则就不太合适,例如:
a(?<=b)永远不可能成立,因为a!=b;
(?=a)b永远不可能成立,因为a!=b;
$ echo 'foobar' | grep -o -P '(?!foo)bar' #它不能用于查找之前出现所有不是 ”foo” 的 ”bar” 匹配, 它会查找到任意的 ”bar” 出现的情况, 因为 (?!foo) 这个断言在接下来三个字符时 ”bar” 的时候是永远都 TRUE 的(因为foo!=bar)。
bar
$ echo '!abcae20a' | grep -o -P '\w{4,}(?=.*\d)' #0用于支撑后面的前瞻断言
abcae2
$ echo '!abcae20a' | grep -o -P '\w{4,}(?<=\d)' #0不需要拿出来支撑断言
abcae20
$ echo '!abcae20' | grep -o -P '\w{4,}(?<=.*\d)' #后瞻断言必须定长
grep: lookbehind assertion is not fixed length
$ echo '!abcae20' | grep -o -P '(?=.*\d)\w{4,}' #这个前瞻左置断言不定长,而且前瞻过程中没有任何字符约束,也比较诡异,匹配不出结果,最好不要这样写
$ echo 'a2 abcae20a' | grep -o -P '(?=.*\d)\w+' #这个前瞻左置断言不定长,而且前瞻过程中没有任何字符约束,也比较诡异,只匹配出部分结果,最好不要这样写
a2
$ echo '!abcae20' | grep -o -P '(?=\w+\d)\w{4,}' #这个前瞻断言虽然不定长,但是里面的字符都在修饰项的范畴内
abcae20
$ echo 'a2 ab!cae20a' | grep -o -P 'a(?=.*\d)\w+' #注意对于第2组结果'ab',里面并不包含数字,但是其后面有数字20,因此前瞻断言成立
a2
ab
ae20a
$ echo '!abcae20' | grep -o -P '.*?(?=.*\d)\w{4,}' #可以用最开始的.*来吸收不属于\w的字符
!abcae20
$ echo '!a21ed0' | grep -o -P '(?=(?:\w*?\d){3,})\w{6,}' #实现了最终想要的功能:一个字符串至少有6个字符,而且至少有3个数字
a21ed0
总结:前瞻断言放在修饰项后面比较容易理解;放在修饰项前面时:主要用于概括性的规定修饰项里包含的内容,因此此时的前瞻断言里允许出现的字符应该都是修饰项里可接受的字符
更多详细介绍可以参见:http://www.php.net/manual/zh/reference.pcre.pattern.syntax.php