今天首先要讲的是如何利用正则去重复,例如有一串字符串'122345333',如何去其中的重复元素?

我们先来看一下/((\d)\d*?)\2/g,这个正则匹配的是开头一个数字,中间有或无数字,结尾还是那个数字,例如1231或者11,然后把它替换成去除最后一位重复的,例如:1231替换成123,11替换成1。

那为什么还要一个循环呢?因为可能遇到多个在一起的数字,例如字符串:'123114',根据刚才的说法,第一次匹配1231变成123,但是原字符串变成了12314,还是有首位相同的字符串,需要再次匹配替换。

引发问题

试问如何解释下面的正则结果:

字符串:878 正则表达式:(?<=(\d)\d*)\1匹配结果:8        字符串:9878 正则表达式:(?<=(\d)\d*)\1匹配结果:

字符串:878 正则表达式:(?<=(\d)\d*?)\1匹配结果:        字符串:9878 正则表达式:(?<=(\d)\d*?)\1匹配结果:

字符串:878 正则表达式:(?<=(\d))\d*\1匹配结果:78      字符串:9878 正则表达式:(?<=(\d))\d*\1匹配结果:78

字符串:878 正则表达式:(?<=(\d))\d*?\1匹配结果:78     字符串:9878 正则表达式:(?<=(\d))\d*?\1匹配结果:78

看到这个问题有点头大啊,其实我刚开始看到也是,不过仔细分析就能发现其中的精妙之处。首先这个问题是上面那个问题的延伸问题,就是一个人想通过下面的正则去完成字符串去重,但是却发现了一些难以解释的结果,因此就把它们单独拿出来讲下了。

首先(?<=)这个是匹配一个前面的位置,常见的用法如下:

/(?<=hello) world/匹配world前面有个hello,如果有那么就匹配成功。

其次*是贪婪匹配,*?是惰性匹配。现在我们来看一下878匹配(?<=(\d)\d*)\1:

background Layer 1 8 7 8 左边没有,匹配失败,右移 正则:(?<=(\d)\d*)\1 8 7 8 \d匹配8,\d*匹配没有,因此\1为8, 但是8后面是7,因此匹配失败 8 7 8 \d匹配7,但是因为\d*为贪婪匹配,要匹配最多 因此\d匹配8,\d*匹配7,那么\1为8,刚好和右边匹配

这里要注意第三步,(?<=)是从右向左匹配的,第三步里面,因为\d*为贪婪匹配,所以(?<=(\d)\d*)会匹配878前面的两个数字87。并且注意,\d*匹配了7之后就没有回溯了,就是说\d*不会再去匹配空的情况了,因为(?<=)使他丢弃了回溯。现在这么说可能还不明白,看看9878可能就明白了。

background Layer 1 9 8 7 8 左边没有,匹配失败,右移 正则:(?<=(\d)\d*)\1 9 8 7 8 \d匹配9,\d*匹配没有,因此\1为9, 但是9后面是8,因此匹配失败 9 8 7 8 \d匹配8,但是因为\d*为贪婪匹配,要匹配最多 因此\d匹配9,\d*匹配8,那么\1为9,但是8后是7,失败 9 8 7 8 \d匹配7,但是因为\d*为贪婪匹配,要匹配最多 因此\d匹配9,\d*匹配87,那么\1为9,但是7后是8,失败 9 8 7 8 \d匹配8,但是因为\d*为贪婪匹配,要匹配最多 因此\d匹配9,\d*匹配878,那么\1为9,后面没了,失败

奇怪的是878都可以匹配成功,但是为什么9878匹配不成功,原因就是因为\d*是贪婪匹配,那么每次匹配后\1都是9,而且\d*的贪婪匹配在(?<=)中无法回溯,因此匹配失败。

其实我们可以发现\d*在(?<=)里不会发生回溯,那我们看看\d*在外面表现如何,这里拿9878做例子:(878和9878一样)

background Layer 1 9 8 7 8 左边没有,匹配失败,右移 正则:(?<=(\d))\d*\1 9 8 7 8 \d匹配9,\d*贪婪匹配878,\1为9,失败 9 8 7 8 \d匹配9,\d*回溯至87,\1为9,失败 9 8 7 8 \d匹配9,\d*回溯至8,\1为9,失败 9 8 7 8 \d匹配9,\d*回溯至无,\1为9,失败 \d*回溯 9 8 7 8 \d匹配8,\d*贪婪匹配78,\1为8,失败 9 8 7 8 \d匹配8,\d*回溯至7,\1为8,成功 \d*回溯

我们发现在\d*在外面的时候会发生回溯,就是从最长的开始匹配,逐步减少个数,逐个匹配。

现在我们来看一下\d*?遇到(?<=)的表现,根据刚才我们知道\d*在(?<=)中是只会匹配最长的然后不会回溯,那么对应的\d*?只会匹配最短的,然后不回溯,这里我们拿9878做例子:(878和9878一样的)

background Layer 1 9 8 7 8 左边没有,匹配失败,右移 正则:(?<=(\d)\d*?)\1 9 8 7 8 \d匹配9,\d*?匹配无,\1表示9,失败 9 8 7 8 \d匹配8,\d*?惰性匹配,匹配无,\1表示8,失败 9 8 7 8 \d匹配7,\d*?惰性匹配,匹配无,\1表示7,失败 9 8 7 8 \d匹配8,\d*?惰性匹配,匹配无,\1表示8,失败

我们可以看到,\d*?遇到(?<=)的时候,惰性匹配导致直接匹配无,并且不会发生回溯,因此(?<=(\d)\d*?)和(?<=(\d))是一样的。

相比之下,如多\d*?在(?<=)外面的话,虽然它是先匹配无,但是它是会发生回溯的:

background Layer 1 9 8 7 8 左边没有,匹配失败,右移 正则:(?<=(\d))\d*?\1 9 8 7 8 \d匹配9,\d*?惰性匹配无,\1表示9,失败 9 8 7 8 \d匹配9,\d*?回溯到8,\1表示9,失败 9 8 7 8 \d匹配9,\d*?回溯到87,\1表示9,失败 9 8 7 8 \d匹配9,\d*?回溯到87,\1表示9,失败 \d*?回溯 9 8 7 8 \d匹配8,\d*?惰性匹配无,\1表示8,失败 9 8 7 8 \d匹配8,\d*?回溯匹配7,\1表示8,成功 \d*?回溯

其他文章

0
我要评论

评论

返回
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

提交
还可以输入500个字