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)
——所以,還是有必要求個甚解。