您的位置:首页 > Web前端 > JavaScript

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

2016-02-29 17:00 573 查看
碰到一个特别的需求,就是有一段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)


——所以,还是有必要求个甚解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: