考虑使用正向回顾:
var re = /(?<=foo)bar/gi;
这是 Plunker 中无效的正则表达式。为什么?
2020 更新:Javascript 实现开始原生支持正则表达式lookbehinds。 RegExp Lookbehind Assertions 的提案草案已被 ECMAScript 2021 的 ECMA-262 草案规范接受,已在 Chrome 62+ 中的 V8 的 Irregexp 中实现(2017 年 10 月 17 日发布),这是通过 Firefox 78+ 中的 Irregexp 的 shim 层拾取的(ESR,于 2020 年 6 月 30 日发布)。其他 JS 解释器将会跟进。
在此处查看更详细的支持列表。
正则表达式lookbehinds的支持,例如(?<=…)
(正)和
(?<!…)
(负),但这并不意味着您仍然无法在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"
}
负向后查找可以在没有全局标志的情况下完成,但只能使用固定宽度,并且you必须计算该宽度(这可能会因为alternations而变得困难)。使用 (?!foo).{3}(bar)
会更简单且大致等效,但它不会匹配以“rebar”开头的行,因为
.
无法匹配换行符,因此我们需要上面的代码的交替来匹配字符前具有“bar”的行四个。如果您需要可变宽度,请使用下面的全局解决方案,并在
break
节的末尾放置
if
。 (这个限制很常见。.NET、vim和JGsoft是唯一支持可变宽度后向查找的正则表达式引擎。PCRE、PHP和Perl仅限于固定宽度。Python需要一个备用正则表达式模块来支持这一点。也就是说,下面的解决方法的逻辑应该适用于所有支持正则表达式的语言。) 匹配(全局)
g
修饰符,全局匹配),您必须在每次循环迭代中重新定义
matcher
变量,并且必须使用
RegExp.exec()
(与RegExp创建在循环之前),因为String.match()
以不同方式解释全局修饰符并且将创建无限循环!
全球正向回顾:
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"
}
“东西”当然可能包括填充数组以供进一步使用。
全球负面回顾:
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 中模仿 Lookbehind 是一篇很棒的文章,描述了如何做到这一点。
它甚至还有一个指向在 JS 中实现此功能的短函数集合
的后续函数。
在
String.replace()
中实现lookbehind要容易得多,因为您可以创建一个
匿名函数作为替换并处理该函数中的lookbehind逻辑。 这些在第一场比赛中起作用,但只需添加
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
是空的)。这与其他动态宽度负回顾解决方法具有相同的警告,并且通过使用固定宽度方法进行类似的修复。
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);