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

    1. var str='122345333';
    2. while((str!=(str=str.replace(/((\d)\d*?)\2/g,'$1')))){}
    3. console.log(str);

    我们先来看一下/((\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

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

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

    1. var str='hello world';
    2. str.match(/(?<=hello) world/)
    3. /*
    4. 结果: world
    5. */

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

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

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

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

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

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

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

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

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

    回到顶部
    我要评论

    所有评论

      相关文章