正则表达式负面lookbehind在JavaScript中无效

Jac*_*kie 4 javascript regex regex-lookarounds

考虑:

var re = /(?<=foo)bar/gi;
Run Code Online (Sandbox Code Playgroud)

它是Plunker中的无效正则表达式.为什么?

Ada*_*atz 15

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"
}
Run Code Online (Sandbox Code Playgroud)

固定宽度负向后视匹配:

// from /(?<!foo)bar/i
var matcher = mystring.match( /(?!foo)(?:^.{0,2}|.{3})(bar)/i );
if (matcher) {
  // do stuff with matcher[1] ("bar"), knowing that it does not follow "foo"
}
Run Code Online (Sandbox Code Playgroud)

负面观察可以在没有全局标志的情况下完成,但只能使用固定宽度,并且必须计算该宽度(这可能会因交替而变得困难).使用(?!foo).{3}(bar)将更简单和大致相同,但它不匹配以"钢筋"开头的.行,因为无法匹配换行符,因此我们需要上述代码的交替来匹配在字符四之前具有"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"
}
Run Code Online (Sandbox Code Playgroud)

"Stuff"当然可以包括填充阵列以供进一步使用.

全球负面观察:

var re = /(foo)?bar/gi;  // from /(?<!foo)bar/gi
while ( matcher = re.exec(mystring) ) {
  if (!matcher[1]) {
    // do stuff with matcher[0] ("bar"), knowing that it does not follow "foo"
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在某些情况下,这并不能完全代表负面的背后.考虑/(?<!ba)ll/g匹配Fall ball bill balll llama.它只能找到所需的四个匹配中的三个,因为当它解析时balll,它会找到ball并继续一个字符l llama.这只发生在末端的部分匹配可能干扰不同端的部分匹配时(balll中断(ba)?llfoobarbar很好(foo)?bar)唯一的解决方法是使用上述固定宽度方法.

更换

在JavaScript中有一篇名为Mimicking Lookbehind的精彩文章描述了如何做到这一点.
它甚至还有一个后续内容,指向在JS中实现此功能的组简短函数.

实现lookbehind in String.replace()更容易,因为您可以创建一个匿名函数作为替换并处理该函数中的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) }
);
Run Code Online (Sandbox Code Playgroud)

这需要目标字符串并替换的情况下,barbaz,只要他们遵循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") }
);
Run Code Online (Sandbox Code Playgroud)

这基本上是相同的,但由于它是一个负面的背后,它在$1失踪时起作用(我们不需要在$1 + "baz"这里说因为我们知道$1是空的).

这与其他动态宽度负向后视解决方法具有相同的警告,并且通过使用固定宽度方法类似地修复.


Wik*_*żew 1

这是一种在 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);
Run Code Online (Sandbox Code Playgroud)

  • 众所周知,JS 中的 HTML 应该使用 DOM 解析器进行处理,并且正则表达式应该仅针对 tex 节点运行。请参阅[*RegEx 匹配除 XHTML 自包含标签之外的开放标签*](http://stackoverflow.com/questions/1732348/regex-match-open-tags- except-xhtml-self-contained-tags)。 (3认同)