js正则表达式中关于零宽断言的奇异现象

碰到一个特别的需求,就是有一段Sql Server 的 SQL片段,内容大概就是所有JOIN表的集合,要求把这个SQL片段分割成数组,每个元素就是包含单个表的字符串。

例如:

SQL = INNER JOIN Sale b ON 1=1 LEFT JOIN OutStock c ON 1=1 RIGHT JOIN BunissMan c ON 1=1 

分割成:

["INNER JOIN Sale b ON 1=1 ","LEFT JOIN OutStock c ON 1=1 ","RIGHT JOIN BunissMan c ON 1=1 "]

还好理解吧?


实现的方式其实挺多,循环遍历分割、正则分割等等。

为了感觉高大上一点,决定了正则。

就是在实验的过程中,发现了一个奇异的现象。

本来想测试一下各种JOIN的匹配情况,于是采用正则表达式如下:

/(?=INNER)|(?=LEFT)|(?=RIGHT)|(?=FULL OUTER) JOIN/

采用match,没有匹配到我想要的东西,但是,split却出来了结果:

测试源码:

var SQL = 'INNER JOIN Sale b ON 1=1 LEFT JOIN OutStock c ON 1=1 RIGHT JOIN BusniessMan c ON 1=1 ';
var RegExpStr = /(?=INNER)|(?=LEFT)|(?=RIGHT)|(?=FULL OUTER) JOIN/;
var MatchAll = SQL.match(RegExpStr)
console.log(MatchAll)
var SplitArr = SQL.split(RegExpStr)
console.log(SplitArr)

测试结果:

["", index: 0, input: "INNER JOIN Sale b ON 1=1 LEFT JOIN OutStock c ON 1=1 RIGHT JOIN BusniessMan c ON 1=1 "] 
["INNER JOIN Sale b ON 1=1 ", "LEFT JOIN OutStock c ON 1=1 ", "RIGHT JOIN BusniessMan c ON 1=1 "]

着实让我又惊又喜。

喜的是,这个需求这么容易结束了。

惊的是,不知道这是怎么得到的结果。

在python里面测试了相同的零宽断言,发现是不能进行分割的。


本着研究的精神继续探索。

var SomeStr = '12345129345'
console.log(SomeStr.split(/234/))             // ["1", "5129345"]
console.log(SomeStr.split(/2(?=3)/))          // ["1", "345129345"]
console.log(SomeStr.split(/(?=2)34/))         // ["12345129345"]
console.log(SomeStr.split(/(?=2)234/))        // ["1", "5129345"]
console.log(SomeStr.split(/(?=2)|(?=9)3/))    // ["1", "23451", "29345"]
console.log(SomeStr.match(/(2|9)3/g))         // ["23", "93"]
console.log(SomeStr.match(/(?=23)|(?=93)/g))  // ["", ""]
console.log(SomeStr.split(/(?=23)|(?=93)/))   // ["1", "234512", "9345"]


分析一下:

第一行将234作为分隔符分割了,很好理解。

第二行使用了零宽断言,能匹配2后面紧接着是3的的“分隔符”,所以将前面的“2”作为分隔符,而后面的2接着9,不能分隔。

第三行一开始困惑了我,理论上应该能将前面的234分割了呀,实际上不是的,"(?=2)3"这个正则匹配断言所在位置为2,但是后面却是3,所以永远匹配不到。

第四行解释了第三行的问题,断言当前所在位置为2,因此能匹配到234这个字符串。

第五行看懂了没,根据上面3、4两行的经验,这里不是表示能匹配23和93,而是仅仅能匹配 “2和一个空字符串(并且不包含2)” 这个结果,93是永远匹配不到的,因为(?=9)后面必须接9而断言后面却接了3;并且竖线“或关系”也不是按照我们头脑中思考的分成 “(?=2)3 or (?=9)3” 而是 “(?=2) or (?=9)3” ;所以只能匹配前者 "(?=2)"。

第六行指示了正确使用竖线的方式。如果是 /2|93/ ,那么结果就是2,2,93。

第七行和第八行展示了我们正确使用零宽断言分割的方法。


所以,总结来说,并非是bug,而只是我不太理解零宽断言而已,并且这里很容易跳进坑里面。

By the way,这么弄来,我需求实现的代码错了,应该这么写:

var SQL = 'INNER JOIN Sale b ON 1=1 LEFT JOIN OutStock c ON 1=1 RIGHT JOIN BusniessMan c ON 1=1 ';
var RegExpStr = /(?=INNER JOIN)|(?=LEFT JOIN)|(?=RIGHT JOIN)|(?=FULL OUTER JOIN)/;
var SplitArr = SQL.split(RegExpStr)
console.log(SplitArr)

——所以,还是有必要求个甚解。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值