请考虑以下内容:
var re = /(?<=foo)bar/gi;
在 Plunker 中,这是一种无效的正则表达式。为什么呢?
请考虑以下内容:
var re = /(?<=foo)bar/gi;
2020年更新: Javascript实现开始本地支持正则表达式的前瞻。ECMAScript 2021的RegExp Lookbehind Assertions草案提议已被接受到ECMA-262 draft specs中,并在Chrome 62+(发布于2017-10-17)中通过V8的Irregexp实现,这已经通过Irregexp的shim层在Firefox 78+(ESR,发布于2020-06-30)中得到了应用。其他JS解释器也将跟进。
JavaScript不支持正则表达式后向断言,例如(?<=…)
(正向)和(?<!…)
(负向),但这并不意味着您不能在JavaScript中实现这种逻辑。
// from /(?<=foo)bar/i
var matcher = mystring.match( /foo(bar)/i );
if (matcher) {
// do stuff with matcher[1] which is the part that matches "bar"
}
// from /(?<!foo)bar/i
var matcher = mystring.match( /(?!foo)(?:^.{0,2}|.{3})(bar)/i );
if (matcher) {
// do stuff with matcher[1] ("bar"), which does not follow "foo"
}
(?!foo).{3}(bar)
将更简单且大致相等,但它不会匹配以“rebar”开头的行,因为.
无法匹配换行符,所以我们需要上面的代码中的替代方案来匹配在第四个字符之前具有“bar”的行。if
语句的末尾放置一个break
。(这种限制非常普遍。.NET,vim和JGsoft是唯一支持可变宽度回溯的正则表达式引擎。only。PCRE,PHP和Perl仅支持固定宽度。Python需要一个替代正则表达式模块来支持此功能。话虽如此,下面的解决方法逻辑应该适用于所有支持正则表达式的语言。)
当您需要在给定字符串中循环每个匹配项(全局匹配,使用g
修饰符)时,您必须在每个循环迭代中重新定义matcher
变量,并且必须使用{{link1:RegExp.exec()
}}(在循环之前创建{{link2:RegExp}}),因为{{link3:String.match()
}}会以不同的方式解释全局修饰符{{link4:并导致无限循环!
var re = /foo(bar)/gi; // from /(?<=foo)bar/gi
while ( matcher = re.exec(mystring) ) {
// do stuff with matcher[1] which is the part that matches "bar"
}
“Stuff”当然可以包括填充数组以供进一步使用。
var re = /(foo)?bar/gi; // from /(?<!foo)bar/gi
while ( matcher = re.exec(mystring) ) {
if (!matcher[1]) {
// do stuff with matcher[0] ("bar"), which does not follow "foo"
}
}
/(?<!ba)ll/g
与 Fall ball bill balll llama
匹配的情况。它只会找到三个所需的四个匹配项,因为当它解析 balll
时,它找到了 ball
,然后在 l llama
处延迟了一个字符。只有当结尾的部分匹配可能会干扰不同结尾的部分匹配时才会发生这种情况( balll
破坏了(ba)?ll
但 foobarbar
对(foo)?bar
没有问题)。唯一的解决方法是使用上述固定宽度方法。
模仿JavaScript中的后顾断言是一篇很棒的文章,描述了如何实现这个功能。
它甚至有一个后续,指向JS中实现这个功能的短函数集合。
在String.replace()
中实现后顾断言要容易得多,因为你可以创建一个匿名函数作为替换,并在该函数中处理后顾断言逻辑。
这些适用于第一个匹配项,但只需添加g
修饰符即可使其全局匹配。
// assuming you wanted mystring.replace(/(?<=foo)bar/i, "baz"):
mystring = mystring.replace( /(foo)?bar/i,
function ($0, $1) { return ($1 ? $1 + "baz" : $0) }
);
这段代码将目标字符串中的bar
替换为baz
,但前提是它们跟在foo
后面。如果是这样,就会匹配到$1
,然后三元运算符(?:
)返回匹配到的文本和替换文本(但不包括bar
部分)。否则,三元运算符返回原始文本。
// assuming you wanted mystring.replace(/(?<!foo)bar/i, "baz"):
mystring = mystring.replace( /(foo)?bar/i,
function ($0, $1) { return ($1 ? $0 : "baz") }
);
$1
缺失时起作用(我们不需要在这里说$1 + "baz"
,因为我们知道$1
是空的)。(?<!ba)ll
转换为 (ba)?ll
:一个简单的解决方法是只消耗一个位置并使用前瞻:(ba)?(?=ll).
- Casimir et Hippolyte(?!foo)(?:^.{0,2}|.{3})(bar)
最好写成这样:(?:^.{0,2}|(?!foo).{3})(bar)
(此外,想象一下,如果你不是在处理foo/bar而是在以fooar
开头的字符串中进行处理)。 - Casimir et Hippolyte(?<=ab|abc|abcd)
。 - Casimir et Hippolyte以下是使用JS中的DOM解析HTML字符串并仅在标签外执行替换的方法:
var s = '<span class="css">55</span> 2 >= 1 2 > 1';
var doc = document.createDocumentFragment();
var wrapper = document.createElement('myelt');
wrapper.innerHTML = s;
doc.appendChild( wrapper );
function textNodesUnder(el){
var n, walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
while(n=walk.nextNode())
{
if (n.parentNode.nodeName.toLowerCase() === 'myelt')
n.nodeValue = n.nodeValue.replace(/>=?/g, "EQUAL");
}
return el.firstChild.innerHTML;
}
var res = textNodesUnder(doc);
console.log(res);
alert(res);
var re = /foo(bar)/gi;
。那么真正的问题是什么? - Wiktor Stribiżew(?<!foo)
,正如Wiktor所说,JavaScript不支持后行断言。 - Marcos Casagrande