JS正则表达式之先行零宽断言

曾自己借助阿里云和hexo搭了个站点,现已废弃,过往写的博客暂挪到此处。


title: JS正则表达式之先行零宽断言
subtitle: 只匹配,不返回,不消费
date: 2016-12-13 17:23:03
tags:
- 技术
- Javascript
- 正则
- TODO


点击这里可以直接看我的解析,跳过啰嗦的自我探索过程

JS的正则表达式,关于零宽断言,只有先行断言。

什么叫先行断言呢?

JS犀牛书 里是这样介绍的

字符 含义
(?=p) 零宽正向先行断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符
(?!p) 零宽负向先行断言,要求接下来的字符不与p匹配

例子如下:

var str1 = "bedroom";
var str2 = "bedding";
var reBed = /(bed(?=room))///在我们捕获bed这个字符串时,抢先去看接下来的字符串是不是room
alert(reBed.test(str1));//true
alert(reBed.test(str2))//false

var str1 = "bedroom";
var str2 = "bedding";
var reBed = /(bed(?!room))/  //要来它后面不能是room
alert(reBed.test(str1))//false
alert(reBed.test(str2))//true

看起来很简单是不?
如果你想做一个匹配,字符串 http://www.baidu.com?from=monvhh=endbalabla,我要得到 ?from=monvhh=end中的monvhh,当然这个monvhh可能是任意的字符串

想通过一次正则匹配就得到?from==end中间的字符串,正则如此强大,一定可以。
那么我需要去匹配?from==end,并且我还不要它们。只有零宽断言可以做到了!

var reg = /(?=\?from=)\w+(?=end)/

好简单,好方便是不是!
然而失败了。。。

WHY?!

谷歌良久,得到的结果就是

  • 1)先行断言嘛,只能匹配结尾(网上的例子都是匹配结尾!会去匹配前置的,都是零宽负向先行断言(?!p))。
  • 2)后行断言可以实现我这个思路。
  • 3)零宽断言只占位,不消费。

只能匹配结尾,为什么?根据它的定义,不至于啊。
是因为“都”字的原因么?
但是

var reg = /Java(?=Script)/;
var str1 = 'JavaScript';
var str2 = 'JavaScripter';
reg.test(str1);//true
reg.test(str2);//true

str2的结尾除了Script还有er啊,并不是结尾完全匹配Script,那我在匹配这个之后再继续匹配别的,怎么就不行了呢?

3)解释了这个问题。但我当时一直没想通,跟我这个有什么关系,直到做了大量的测试。。。

var reg = /(?=\?from=)\w+(?=end)/

这个例子,我一直以为是(?=\?from=)这部分用错了,是先行零宽断言的错。
确实是它的错,但并不是在解析到它这里出错的。
而是在它之后的\w+,如果换成\S就能test成功了。(仅仅是test成功了)

为什么?monvhh符合\w啊。

原因是:零宽断言只占位,不消费。即
匹配完(?=\?from=)之后的字符串是?from=monvhh=endbalabla

注意:此字符串的意思是:待匹配字符串。
如果你拿(?=\?from=)单独去test字符串http://www.baidu.com?from=monvhh=endbalabla,是true,如果用该字符串去match该正则,只能得到一个空字符串,对,还不是null。
因为零宽断言就是只匹配,但并不得到这个匹配的结果。这就是我为什么用它的原因嘛,我需要判断字符串里有你,但是我不要你。
也就是只占位,不消费。它验证过,但是并没有把它消费掉。

所以,此时继续匹配\w+就会报错,匹配\S+却可以,应为此时的字符串里还有?和=。

这就是“先行断言”这个词的关键了。
它就像一个if语句,它只判断,在我当前位置之后,有某个字符串。但并不改变这个字符串,也不对结果有任何影响。
而其他正则匹配,是一个处理原始字符串的过程(不改变这个字符串,但是改变匹配之后的结果result)。

这是我理解的,正则匹配就像是一个匹配字符串返回result的结果,这个result在一开始等于这个字符串,在匹配的过程中从左至右削减:匹配正则中第一个位置成功之前的,削掉;然后在目前的result中,就继续匹配正则中下一个位置;若没匹配上,就把目前的result中削减至result中当前正在匹配的位置(即之前匹配该正则前部分成功的那些字符串也抛弃掉),在剩下的result中继续匹配。
(以上理解仅为只匹配一次的情况,分组和全局模式应当是在此基础上组合)
如果削减到最后都没有匹配上这个正则,那么test的话就是false了。

而断言却不同,它匹配上,却不削减。

它只做了if这一步,其他什么都没做(相较于以上处理result的过程,它对result不作任何处理)。

在我之前的理解中,只知道它匹配上,却不返回。
对,它既不返回,也不削减。

如果先行断言用在正则的最后,那么它不返回。
如果先行断言,并不是在正则的尾部,那么,虽然它不返回了,但是因为剩下部分正则还在继续匹配,它没有削减result,所以接下来的正则,还得继续匹配该先行断言明明已经匹配上的部分。相当于只卡了个关口(if语句),嘛都没做。当然这就是它的魅力所在,我是说定义。。

这是正向先行断言。
负向先行断言的话,就没有这个削减的问题,因为它本来就被期待匹配不上,当然不会削减。但用它的时候也要小心,毕竟跟[^...]的功能不一样,因为[^...]虽然是期待匹配不上,但是它却占了一个位置,在它之后部分的正则,从它这占了一个位置的字符串之后开始匹配。

<span id="analysis">用我的方式理解正则匹配(包括零宽断言)</span>:

只是简单的匹配逻辑,不考虑什么分组、匹配位置、修饰符等;

var reg,str;
//正则reg,待匹配字符串str;
//reg是一串针对连续位置的匹配规则,我在强调连续两个字,断言也不能;
//str是一串连续的字符串;

var reg_pointer = 0,str_pointer = 0;
//指针reg_pointer;指针str_pointer;
//分别为正则reg,待匹配字符串str的指针,初始为0,即首位;
var str_length = str.length,reg_length = reg.length;
//此处reg.length仅表示正则reg的占位,先把零宽断言也算,方便对比;

var result;
//result = str.substring(str_pointer,str_length);所以str_pointer可以表达我说的削减的意思;
//了解正则表达式中的贪婪、惰性、支配性之后,知道我所说的削减,其实就是贪婪性匹配的特质!!

for( str_pointer < str.length && reg_pointer < reg_length ){
    var bool = str[str_pointer]匹配reg[reg_pointer];
    //reg[reg_pointer]仅表示正则中的第reg_pointer的占位,先把零宽断言也算,方便对比;

    
    if( reg[reg_pointer] != 先行零宽断言 && bool === false ){
        //重新再来,该轮匹配失败,从接下来的字符串再继续匹配整条正则
        reg_pointer=0;
        str_pointer++;
        //与先行零宽断言的一致,匹配失败都是一样的
    }

    if( reg[reg_pointer] != 先行零宽断言 && bool === true ){
        //继续匹配,削减
        reg_pointer++;
        str_pointer++;
    }

    if( reg[reg_pointer] == 先行零宽断言 && bool === false ){
        //重新再来,该轮匹配失败,从接下来的字符串再继续匹配整条正则
        reg_pointer=0;
        str_pointer++;
    }
    if( reg[reg_pointer] == 先行零宽断言 && bool === true ){
        //继续匹配,但不削减
        reg_pointer++;
        //所以字符串的指针不变,但是正则进行到下一个占位了。
        //这就是零宽断言不占位的真谛了。
    }
    result = str.substring(str_pointer,str_length);
}

//匹配失败
//没有reg_length-1是因为如果最后一次还是匹配成功,还会再做一次reg_pointer++;
if( reg_pointer < reg_length ){
    result = undefined;
}
return result;

结果

所以它实现不了我的想法。

最后我通过分组的方式,然后match数组中的第一个分组得到的结果。如下:

var reg = /\?from=(\w+)=end/;
var result = 'http://www.baidu.com?from=monvhh=endbalabla'.match(reg);
var whatiwant = result[1];//monvhh

TODO 如何在正则中达到跳过的效果。

意即,破坏我在解析正则匹配实现过程中指出的,

reg是一串针对连续位置的匹配规则,我在强调连续两个字,断言也不能;

比如,我想跳过几个字符,再继续匹配,或者我想跳过某几个特定的字符,再继续匹配。怎么搞?

我是说一次性的,不要跟我说分组之后继续。。。或者substring。。。

如果是跳过前面和后面的,可以用后行断言匹配前面,匹配成功但不返回,先行断言匹配后面的,匹配成功但不返回。成功实现要匹配但返回结果却跳过的需求。

然后如果要跳过的是中间的呢?

本文参考
http://www.cnblogs.com/rubylouvre/archive/2010/03/09/1681222.html
http://fxck.it/post/50558232873

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容

  • 于八月中旬接到朋友之约要到苏州小聚一下,此时身在盐城的我无法拒绝,因为我们的孩子很久没有见面了!(因为两个孩子是发...
    九宫格格阅读 214评论 0 0
  • .印象较深三个点 1.这节课居然有其它的老师来听课。 2.当看到计算时,感觉到了心理学的高深莫测与个人智商的担忧。...
    口服阅读 141评论 0 0
  • 水波荡漾,湖边的垂柳轻柔地挥着手,夕阳恋恋不舍地挨进西面的大山,余晖落在被风吹皱的水面上,轻盈的在水面上漂游着,...
    6葛丁滟阅读 620评论 0 1
  • 事情是怎么发展到今天这样的呢? 虎眼沉着脸蹲在卧室门口,反省自己的疏忽大意。 那傻小子抱着手机傻笑的时候,它就应该...
    悬诀阅读 224评论 0 3
  • #打卡# 05月13日 周六 天气晴 【维密训练进度】日跑5公里第78天,计划坚持100天;每日4:55前早起坚持...
    做自己的女王Vivian阅读 232评论 4 2