【问题标题】:javascript regex - look behind alternative?javascript regex - 寻找替代方案?
【发布时间】:2011-11-14 15:07:02
【问题描述】:

这是一个在大多数正则表达式实现中都能正常工作的正则表达式:

(?<!filename)\.js$

这匹配 .js 以 .js 结尾的字符串,除了 filename.js

Javascript 没有正则表达式后视。有没有人可以组合一个替代的正则表达式来实现相同的结果并在 javascript 中工作?

这里有一些想法,但需要辅助函数。我希望只用一个正则表达式来实现它: http://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript

【问题讨论】:

  • 如果您只需要检查特定文件名或文件名列表,为什么不只使用两次检查呢?检查它是否以 .js 结尾,如果是,请检查它是否与 filename.js 不匹配,反之亦然。
  • 更新:最新的公共 Chrome 版本 (v62) 包括(可能是实验性的)开箱即用的lookbehinds :D 但请注意,lookbehinds 仍处于提案阶段 3:github.com/tc39/proposal-regexp-lookbehind。因此,可能需要一段时间才能让 JavaScript 到处都支持它。在生产中使用时最好小心!
  • # 更新:ES2018 包括 lookbehind assertions Plus:- dotAll 模式(s 标志)- Lookbehind 断言- 命名捕获组- Unicode 属性转义
  • 只需将 (?&lt;=thingy)thingy 用于正向后视(?&lt;!thingy)thingy 用于负向后视现在它支持它们。
  • @K._ 截至 2018 年 2 月 这不是真的!而且还需要一些时间,因为浏览器和引擎必须实现规范(草案中的当前版本)。

标签: javascript regex


【解决方案1】:

^(?!filename).+\.js 为我工作

测试:

  • test.js 匹配
  • blabla.js 匹配
  • filename.js 不匹配

这个正则表达式的正确解释可以在Regular expression to match string not containing a word?找到

version 1.5 of javascript 开始提供前瞻功能,并受到所有主要浏览器的支持

更新以匹配 filename2.js 和 2filename.js 但不匹配 filename.js

(^(?!filename\.js$).).+\.js

【讨论】:

  • 您链接到的那个问题谈到了一个稍微不同的问题:匹配一个不包含目标词 anywhere 的字符串。这个要简单得多:匹配一个不目标词开头的字符串。
  • 这真的很好,它只会错过以下情况:filename2.js 或 filenameddk.js 或类似的情况。这是不匹配的,但应该是匹配的。
  • @daniel 您要求的是后视,而不是前瞻,您为什么接受这个答案?
  • 给定的与a.js不匹配
  • 带有后视功能的原始正则表达式与2filename.js 不匹配,但此处给出的正则表达式匹配。更合适的是^(?!.*filename\.js$).*\.js$。这意味着,匹配任何*.js 除了 *filename.js
【解决方案2】:

编辑:从 ECMAScript 2018 起,lookbehind assertions (even unbounded) are supported natively

在以前的版本中,您可以这样做:

^(?:(?!filename\.js$).)*\.js$

这会显式地执行后向表达式隐式执行的操作:检查字符串的每个字符是否后向表达式加上正则表达式不匹配,然后才允许该字符匹配。

^                 # Start of string
(?:               # Try to match the following:
 (?!              # First assert that we can't match the following:
  filename\.js    # filename.js 
  $               # and end-of-string
 )                # End of negative lookahead
 .                # Match any character
)*                # Repeat as needed
\.js              # Match .js
$                 # End of string

另一个编辑:

我很痛苦地说(特别是因为这个答案已经得到了如此多的支持)有一种更简单的方法来实现这个目标。无需检查每个字符的前瞻:

^(?!.*filename\.js$).*\.js$

同样有效:

^                 # Start of string
(?!               # Assert that we can't match the following:
 .*               # any string, 
  filename\.js    # followed by filename.js
  $               # and end-of-string
)                 # End of negative lookahead
.*                # Match any string
\.js              # Match .js
$                 # End of string

【讨论】:

  • 适用于很多情况,除非有前面的字符,例如:filename.js (works-nomatch) filename2.js (works-match) blah.js (works - match) 2filename.js (不起作用 - 不匹配) --- 话虽如此,后视有同样的限制,我直到现在才意识到......
  • @daniel:嗯,你的正则表达式(带有后视)也不匹配2filename.js。我的正则表达式在与您的示例正则表达式完全相同的情况下匹配。
  • 原谅我的天真,但是这里的非捕获组有什么用吗?我一直都知道这仅在尝试收集回参考以替换字符串时才有用。据我所知,这也可以工作 ^(?!filename\.js$).*\.js$
  • 不完全是,正则表达式仅在字符串的开头检查“filename.js”。但是^(?!.*filename\.js$).*\.js$ 会起作用。尝试考虑可能仍然需要 ncgroup 的情况......
  • 这种做法可以概括为:与其往X后面看,不如往前面看X前面的每一个字符?
【解决方案3】:

假设您要查找前面没有unsigned 的所有int

支持消极的后视:

(?<!unsigned )int

不支持负后视:

((?!unsigned ).{9}|^.{0,8})int

基本上的想法是抓取 n 个前面的字符并排除匹配负前瞻,但也匹配前面没有 n 个字符的情况。 (其中 n 是后视的长度)。

所以有问题的正则表达式:

(?<!filename)\.js$

将转化为:

((?!filename).{8}|^.{0,7})\.js$

您可能需要使用捕获组来找到您感兴趣的字符串的确切位置,或者您不想用其他内容替换特定部分。

【讨论】:

  • 我刚刚将这个:(?&lt;!barna)(?&lt;!ene)(?&lt;!en)(?&lt;!erne) (?:sin|vår)e?(?:$| (?!egen|egne)) 转换为 (?!barna).(?!erne).(?!ene).(?!en).. (?:sin|vår)e?(?:$| (?!egen|egne)),这可以满足我的需求。只是将其作为另一个“真实世界”场景提供。见link
  • 我想你的意思是:((?!unsigned ).{9}|^.{0,8})int
  • @pansay 是的。谢谢你。我刚刚更正了我的回复。
  • 感谢您提供更笼统的答案,即使在需要匹配文本深处的情况下(初始 ^ 不切实际)也有效!
【解决方案4】:

如果您可以向前看但可以向后看,您可以先反转字符串,然后再向前看。当然,还需要做更多的工作。

【讨论】:

  • 这个答案真的可以使用一些改进。对我来说,这更像是一条评论。
【解决方案5】:

下面是一个积极的后向 JavaScript 替代方案,展示了如何捕获以“Michael”作为名字的人的姓氏。

1) 鉴于此文本:

const exampleText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";

获取名为 Michael 的人的姓氏数组。 结果应该是:["Jordan","Johnson","Green","Wood"]

2) 解决方案:

function getMichaelLastName2(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(person.indexOf(' ')+1));
}

// or even
    .map(person => person.slice(8)); // since we know the length of "Michael "

3) 检查解决方案

console.log(JSON.stringify(    getMichaelLastName(exampleText)    ));
// ["Jordan","Johnson","Green","Wood"]

在这里演示:http://codepen.io/PiotrBerebecki/pen/GjwRoo

您也可以通过运行下面的 sn-p 来尝试一下。

const inputText = "Michael, how are you? - Cool, how is John Williamns and Michael Jordan? I don't know but Michael Johnson is fine. Michael do you still score points with LeBron James, Michael Green Miller and Michael Wood?";



function getMichaelLastName(text) {
  return text
    .match(/(?:Michael )([A-Z][a-z]+)/g)
    .map(person => person.slice(8));
}

console.log(JSON.stringify(    getMichaelLastName(inputText)    ));

【讨论】:

    【解决方案6】:

    这是Tim Pietzcker's answer 的等效解决方案(另见相同答案的 cmets):

    ^(?!.*filename\.js$).*\.js$
    

    意思是,匹配*.js,除了*filename.js

    要获得此解决方案,您可以检查否定后瞻排除了哪些模式,然后使用否定前瞻准确排除这些模式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-29
      • 1970-01-01
      • 2018-10-05
      • 2022-01-09
      • 2021-12-14
      • 2019-03-24
      • 2021-05-11
      相关资源
      最近更新 更多