zhangguanzhang's Blog

正则零宽断言的一个注意点

字数统计: 795阅读时长: 3 min
2017/04/26

正则的零宽断言的一个冷门知识点

一般来说’(?<’用在左边,’(?’用在右边,但是也有人反着用(倒不如说可以反着用)
下面现象看例子

1
2
3
4
5
6
7
[root@guan temp]# cat -n txt
1 123,asdasd,23sfsdf,
2 123,1nd.
3 ,addfsdf,hjgfj
4 1,23t.fdgdfg,dfdg
5 a,5ggh
6 .asf,

先来一个正常的,匹配一个字符后面逗号的到结尾

1
2
3
4
5
6
7
[root@guan temp]# grep -Pon '.(?=,).+' txt  
1:3,asdasd,23sfsdf,
2:3,1nd.
3:f,hjgfj
4:1,23t.fdgdfg,dfdg
5:a,5ggh
6:f,

然后去掉前面的字符,对比下结果

1
2
3
4
5
6
7
[root@guan temp]# grep -Pon '(?=,).+' txt
1:,asdasd,23sfsdf,
2:,1nd.
3:,addfsdf,hjgfj
4:,23t.fdgdfg,dfdg
5:,5ggh
6:,

可以理解为前面有字符后去掉前面的,或者理解为前面0个字符从这个字符开始,也就是’(?<=正则)’但是保留这个正则部分,不能理解成(?=,).+可以转换为,.+
原因看下面例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@guan temp]# cat txt
1 ,23sfsdf,
2 1nd,
3 ,addfsdf,hjgfj
4 1,23t.fdgdfg,dfdg
5 a,5ggh
6 .asf,
[root@guan temp]# grep -Pon '(?=,).+' txt
1:,23sfsdf,
2:,
3:,addfsdf,hjgfj
4:,23t.fdgdfg,dfdg
5:,5ggh
6:,
[root@guan temp]# grep -Pon ',.+' txt
1:,23sfsdf,
3:,addfsdf,hjgfj
4:,23t.fdgdfg,dfdg
5:,5ggh

总之(?=)用在左边比较鸡肋
说下(?!)用在左边

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@guan temp]# cat -n txt
1 123,asdasd,23sfsdf,
2 123,1nd.
3 ,addfsdf,hjgfj
4 1,23t.fdgdfg,dfdg
5 aa,5ggh
6 .as.f,
7 1al,sd.
8 ,wewqe
[root@guan temp]# grep -Pon '.(?!,).+' txt
1:123,asdasd,23sfsdf,
2:123,1nd.
3:,addfsdf,hjgfj
4:,23t.fdgdfg,dfdg
5:aa,5ggh
6:.as.f,
7:1al,sd.
8:,wewqe
[root@guan temp]# grep -Pon '(?!,).+' txt
1:123,asdasd,23sfsdf,
2:123,1nd.
3:addfsdf,hjgfj
4:1,23t.fdgdfg,dfdg
5:aa,5ggh
6:.as.f,
7:1al,sd.
8:wewqe

正则默认贪婪的,所以一般这样匹配到的是很多(?!)一般用在开头(原因看最后总结),(?!正则)来表示匹配正则部分开头但不包括它或者任意开头
上面就是开头的逗号,可以理解为无论开头有没有逗号都去掉开头的逗号

下面同理(?<=)用在右边比较鸡肋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@guan temp]# cat -n txt
1 123,asdasd,23sfsdf,
2 123,1nd.
3 ,addfsdf,hjgfj
4 1,23t.fdgdfg,dfdg
5 aa,5ggh
6 .as.f,
7 1al,sd.
8 ,wewqe
[root@guan temp]# grep -Pon '.+(?<=,).' txt
1:123,asdasd,2
2:123,1
3:,addfsdf,h
4:1,23t.fdgdfg,d
5:aa,5
7:1al,s
8:,w
[root@guan temp]# grep -Pon '.+(?<=,)' txt
1:123,asdasd,23sfsdf,
2:123,
3:,addfsdf,
4:1,23t.fdgdfg,
5:aa,
6:.as.f,
7:1al,
8:,

同理(?<!)表示无论结尾有没有正则部分,都去掉这部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@guan temp]# cat txt
123,asdasd,23sfsdf,
123,1nd.
,addfsdf,hjgfj
1,23t.fdgdfg,dfdg
aa,5ggh
.as.f,
1al,sd.
,wewqe
[root@guan temp]# grep -Pon '.+(?<!,).' txt
1:123,asdasd,23sfsdf,
2:123,1nd.
3:,addfsdf,hjgfj
4:1,23t.fdgdfg,dfdg
5:aa,5ggh
6:.as.f,
7:1al,sd.
8:,wewqe
[root@guan temp]# grep -Pon '.+(?<!,)' txt
1:123,asdasd,23sfsdf
2:123,1nd.
3:,addfsdf,hjgfj
4:1,23t.fdgdfg,dfdg
5:aa,5ggh
6:.as.f
7:1al,sd.
8:,wewqe

总结的话就是
(?=)和(?<=)用在相反的方向都很鸡肋
(?!)和(?<!)用在相反方向一般是最左边和最右边这样反着用来去掉最开始的或者结尾的匹配的内容

再发个正则技巧总结的链接
一些pcre+perl总结

分组正则向后引用,非捕获向后引用

1
echo aabb | grep -Po "(?<z>.{2})\g'z'"
CATALOG