Javascript 替代品中的正则表达式 Lookbehind

Posted

技术标签:

【中文标题】Javascript 替代品中的正则表达式 Lookbehind【英文标题】:Regex Lookbehind in Javascript Alternatives 【发布时间】:2019-01-18 11:41:09 【问题描述】:

我正在尝试在 JS 中使用以下正则表达式:

(?<=@[A-Z|a-z]+,)\s|(?<=@[A-Z|a-z]+,\s[A-Z|a-z]+)\s(?=\[[A-Z|a-z]+\])

翻译成:

匹配所有以 : 开头的空格

@ 后跟A-Za-z 范围内的任意数量的字符 后跟逗号

匹配前面的所有空格:

@

后跟A-Za-z 范围内的任意数量的字符

后跟逗号 后跟一个空格 后跟A-Za-z 范围内的任意数量的字符

AND 成功:

[ 后跟A-Za-z 范围内的任意数量的字符 ]

但是,JS 不支持lookbehind。是否有任何替代方法可以在 JS 或任何我可以使用的 npm 库中支持上述正则表达式?

所以,如果我们有一个像Hi my name is @John, Doe [Example] and I am happy to be here 这样的句子应该变成Hi my name is @John,Doe[Example] and I am happy to be here。 另外,如果我们有类似Hi my name is @John, Smith Doe [Example] 之类的东西,那应该变成Hi my name is @John,SmithDoe[Example]

【问题讨论】:

javascript 确实支持后视,从 ECMAScript 2018 开始。但几乎没有浏览器支持。 你想替换什么东西吗? @revo 试图替换那些空格,是的 将您的lookbehinds转换为捕获组,并在进行替换时为它们添加反向引用以使它们出现在结果中。 @revo 您能否发布一个示例作为答案 - 不完全确定如何做到这一点 【参考方案1】:

我已经根据新的输入更新了我的答案

console.clear();

var inputEl = document.querySelector('#input')
var outputEl = document.querySelector('#output')

function rep (e) 
  var input = e.target.value;
  var reg = /@([a-z]+?\s*?)+,(\s+[a-z]+)+(\s\[[a-z]+\])?/gim



  matches = input.match(reg);
  var output = input;

  if (matches) 
    replaceMap = new Map()
    for (var i = 0; i < matches.length; i++) 
      var m = matches[i]
        .replace(/\[/, '\\[')
        .replace(/\]/, '\\]')
      replaceMap.set(m, matches[i].replace(/\s+/gm, ''))
    
    for (var [s,r] of replaceMap) 
      output = output.replace(new RegExp(s, 'gm'), r) 
    
  

  outputEl.textContent = output


inputEl.addEventListener('input', rep)
inputEl.dispatchEvent(new Event('input'))
textarea 
  width: 100%; 
  min-height: 100px;
<h3>Input</h3>
<textarea id="input">@Lopez de la Cerda, Antonio Gabriel Hugo David [Author]. I'm the father of @Marquez, Maria</textarea>
<h3>Output (initially empty)</h3>
<p id="output"></p>
<h3>Expected result (on initial input)</h3>
<p>@LopezdelaCerda,AntonioGabrielHugoDavid[Author]. I'm the father of @Marquez,Maria</p>

旧答案内容的备份(出于历史原因)

它至少在 Chrome 中使用这个正则表达式:

/(?<=@[a-z]+,)\s+(?![a-z]+\s+\[[a-z]+\])|(?<=(@[a-z]+,\s[a-z]+))\s+(?=\[[a-z]+\])/gmi

见:https://regex101.com/r/elTkRe/4

但您不能在 PCRE 中使用它,因为它不允许在后视中使用量词。它们必须具有固定宽度。在此处查看右侧的错误:https://regex101.com/r/ZC3XmX/2

无后顾之忧的解决方案

console.clear();

var reg = /(@[A-Za-z]+,\s[A-Za-z]+)(\s+)(\[[A-Za-z]+\])|(@[A-Z|a-z]+,)(\s+)/gm

var probes = [
  '@gotAMatch,     <<<',
  '@LongerWithMatch,        <<<',
  '@MatchHereAsWell,    <<<',
  '@Yup,         <<<<',
  '@noMatchInThisLine,<<<<<',
  '@match, match    [match]<<<<<<<',
  '@    noMatchInThisLine,    <<<<'
]

for (var i in probes) 
  console.log(probes[i].replace(reg, '$1$3$4'))
.as-console-wrapper  max-height: 100% !important; top: 0; 

【讨论】:

现场演示中[fsadsd]后面为什么会匹配空格? @HerrSerker 它也应该匹配@match, match [match]&lt;&lt;&lt;&lt;&lt;&lt;&lt;中第一个匹配之后的空格...应该变成@match,match[match]&lt;&lt;&lt;&lt;&lt;&lt;&lt; @alk 我不是这么理解的。不是所有的空格都应该被替换,而只是那些在给定模式之前或之前和之后的空格。在第二个模式中,空格在前缀中,而不是在要替换(删除)的匹配中 @Alk 您在原始问题中的描述具有误导性。你应该给出输入和预期输出的例子 @HerrSerker 在该示例中,我们有一个以@[a-z|A-Z]+ 开头的空格,因此它也应该被替换。所以如果我们有一个像Hi my name is @John, Doe [Example] and I am happy to be here 这样的句子应该变成Hi my name is @John,Doe[Example] and I am happy to be here - 这更有意义吗?另外,如果我们有类似Hi my name is @John, Smith Doe [Example] 的东西,那应该变成Hi my name is @John,SmithDoe[Example]【参考方案2】:

您需要做的是将后视转换为捕获组,以便将它们包含在替换字符串中(注意设置了不区分大小写的标志 (i)):

(@[a-z]+,)([\t ]*([a-z]+)[\t ]*(?=\[[a-z]+\])|[\t ]+)

如果要删除这些空格,请替换为 $1$3

见live demo here

【讨论】:

怎么在你的演示中,第二个例子中 foo 后面的空格没有被删除 - 它应该变成@foo,bar[john] 更新了答案。请检查。 谢谢 - 是否很难适应这种情况来处理像这样的情况 @foo, bar foo [john] -> @foo,barfoo[john] 所以基本上在 [john] 部分之前有任意数量的可能单词 你必须使用两个replace 调用然后str.replace(/@[a-z]+,([ \t]*[a-z]+)+(\s\[[a-z]+\])?/g, function($0) return $0.replace(/\s+/g, ''); )【参考方案3】:

只需更新您的 Node.js 版本。 Lookbehind 断言是 ECMAScript 2018 的一部分,并且已经在 Chromium 和 Node.js 中实现。根据http://kangax.github.io/compat-table/es2016plus/ 的说法,Chromium 70 和 Node.js 8.10 都有这个功能。

我刚刚在浏览器和 Node.js (v8.11) 中对其进行了测试,可以确认:

node -e "console.log('nothing@xyz, bla'.match(/(?<=@[A-Za-z]+,)\s+/))"

如果您无法更新,则必须使用其他策略,例如捕获和替换,这对于积极的后视来说应该不是一个大问题(消极的更难):

const hit = 'nothing@xyz, bla'.match(/(@[A-Za-z]+,)\s+/)
hit[0].replace(hit[1])

如果没有其他方法,看看这个尝试实现 Lookbehind 的项目(我还没有测试过):http://blog.stevenlevithan.com/archives/javascript-regex-lookbehind

【讨论】:

以上是关于Javascript 替代品中的正则表达式 Lookbehind的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式的可变长度lookbehind-assertion替代方案

js正则匹配替代指定字符(根据img标签的src中的命名规则,用正则表达式替换成下面格式的文字)

js正则匹配替代指定字符(根据img标签的src中的命名规则,用正则表达式替换成下面格式的文字)

JavaScript中的正则表达式

为啥替代品的顺序在正则表达式中很重要?

JavaScript中的正则表达式(终结篇)