Thanks for the answers from Tim Pietzcker and other persons. I was so inspired by their works. However, there is no any ideal solution, I think, for mimicking lookbehind. For example, solution from Pietzcker is limited by $
as EOL, that is, without $
there would get unexpected result:
let str="filename.js main.js 2022.07.01"
console.log( /^(?!.*filename\.js).*\.js/g.exec(str) ) //null
Another limitation is that it is hard to translate multiply lookbehinds, such as:
let reg=/(?<!exP0)exp0 \d (?<!exP1)exp1 \d (?<!exP2)exp2/
How to build a more generic and free method to use lookbehind assertion alternatively? Bellow is my solution.
The core pattern of alternative code is:
(?:(?!ExpB)....|^.{0,3})ExpA <= (?<!ExpB)ExpA
Detail explanation:
(?: # start an unsave group:
(?!ExpB) # Assert a possion who can't match the ExpB
.... # Any string, the same length as ExpB
|^.{0,3} # Or match any string whoes length is less than ExpB
) # End of negative lookahead
ExpA # Match ExpA
For instance:
var str="file.js main.js 2022.07.01"
var reg=/(?:(?!file)....|^.{0,3})\.js/g // <= (?<!file)\.js
console.log( reg.exec(str)[0] ) // main.js
Here is an implement to translate above pattern into a sugar:
var str="file.js main.js 2022.07.01"
var reg=newReg("﹤4?!file﹥\\.js","g") //pattern sugar
console.log(reg.exec(str)[0]) // main.js
function newReg(sReg,flags){
flags=flags||""
sReg=sReg.replace(/(^|[^\\])\\﹤/g,"$1<_sl_>").replace(/(^|[^\\])\\﹥/g,"$1<_sr_>")
if (/﹤\?<?([=!])(.+?)﹥/.test(sReg)){
throw "invalid format of string for lookbehind regExp"
}
var reg=/﹤(\d+)\?<?([=!])(.+?)﹥/g
if (sReg.match(reg)){
sReg=sReg.replace(reg, function(p0,p1,p2,p3){
return "(?:(?"+p2+p3+")"+".".repeat(parseInt(p1))+"|^.{0,"+(parseInt(p1)-1)+"})"
})
}
sReg=sReg.replace(/<_sl_>/g,"﹤").replace(/<_sr_>/g,"﹥")
var rr=new RegExp(sReg,flags)
return rr
}
Two special characters ﹤
( \uFE64 or ﹤
) and ﹥
( \uFE65 or ﹥
) are used to enclose the lookbehind expression, and a number N
counting the length of lookbehind expression must follow the ﹤
. That is ,the syntax of lookbehind is:
﹤N?!ExpB﹥ExpA <= (?<!ExpB)ExpA
﹤N?=ExpB﹥ExpA <= (?<=ExpB)ExpA
To make the pattern above more ES5-like, you can replace ﹤
or ﹥
with parenthesis and remove N
, by writing more code into newReg()
function.
(?<=thingy)thingy
for positive lookbehind and(?<!thingy)thingy
for negative lookbehind. Now it supports them. – Hypersonic