【问题标题】:Why "abcdef" is not matched by (?=abc)def but matched by abc(?=def)? [duplicate]为什么 "abcdef" 不匹配 (?=abc)def 而是匹配 abc(?=def)? [复制]
【发布时间】:2013-06-05 15:06:29
【问题描述】:

在 Javascript 中,我有一个字符串 abcdef,但无法弄清楚这种奇怪的行为:

  • (?=abc)def 与字符串不匹配
  • abc(?=def) 匹配字符串

为什么?

【问题讨论】:

  • 也许你想要 (?:abc)def 代替,它使用非捕获组而不是正向前瞻。
  • @DaoWen 不,我已经试过了……试试"abcdef".replace(/(?:abc)def/, "") 它会替换整个字符串

标签: javascript regex pcre


【解决方案1】:

(?=...) 是一个前瞻,换句话说,它测试它右边的字符串。还要注意,前瞻是不吃字符的零宽度断言。在您的第一个示例中:(?=abc) 这意味着 必须后跟 abc 遇到 def。这就是模式失败的原因。

在第二个示例中,它在abc 之后找到def,然后匹配字符串

【讨论】:

  • 非常清楚地解释了零宽度断言,它也解释了为什么 /(?=def)def/.test('abcdef') 匹配。
【解决方案2】:

MDN definition of lookaheads 在 JavaScript 中

x(?=y)
仅当 'x' 后跟 'y' 时才匹配 'x'。这称为前瞻。

例如,/Jack(?=Sprat)/ 仅在其后跟“Sprat”时才匹配“Jack”。 /Jack(?=Sprat|Frost)/ 仅在 'Sprat' 或 'Frost' 后跟 'Jack' 匹配。但是,“Sprat”和“Frost”都不是匹配结果的一部分。

所以(?=y) 前面有另一个语句,在这种情况下是一个空字符串,那么只有当第一个语句后面跟着第二个语句时它才会匹配。如果没有前导语句,表达式 (?="abc") 将匹配前 3 个字符 abc 而不捕获它们,然后再次检查这些字符是否为 def,这将失败。

【讨论】:

    【解决方案3】:

    (?=abc)def 中,(?=abc) 捕获宽度为零,并且在成功匹配后不会在输入字符串中向前移动光标。该结构只是说向前看接下来的三个字符以查看它们是否为abc,如果它们是则检查这些相同的字符是否为def。此时匹配失败..

    您需要了解正则表达式引擎如何工作才能完成匹配。考虑您的输入字符串abcdef 和您的正则表达式abc(?=def)。引擎首先匹配a,然后将输入字符串内的光标移动到下一个字符并尝试匹配b,因为输入字符串中的光标位于b 上,匹配成功。然后引擎将光标移到输入字符串中并尝试匹配c,因为光标在输入字符串中是c,匹配成功并且输入字符串中的光标再次移动到下一个特点。现在引擎在这一点上遇到(?=def),引擎只是向前看,看看输入字符串中光标所在位置的下三个字符是否实际上是def而不移动光标,它们就是这样并且匹配完成成功了。

    现在考虑输入字符串xyz 和正则表达式x(?=y)Z。正则表达式引擎将光标放在输入字符串中的第一个字母上并检查它是否为x,并发现x,因此它将光标移动到输入字符串中的下一个字符。现在它向前看,看下一个字符是否是y,它是,但引擎不会移动输入文本光标前言,所以输入文本中的光标停留在y。接下来引擎会查看光标是否在字母 z 上,但由于输入文本中的光标仍在字母 y 上,匹配失败。

    您可以在http://www.regular-expressions.info/lookaround.html 阅读更多关于正面和负面预测的信息

    【讨论】:

    • 还没有收到。如果一定要在“之后”,尝试在开头添加^,结果是一样的。
    • 更新了解释以涵盖正则表达式引擎如何处理匹配成功的匹配并显示如果将前瞻插入示例表达式的中间会发生什么。关键在于跟踪光标在输入文本中的位置。
    • 很棒的解释!
    【解决方案4】:

    根据您对我的评论的回复,我认为您想要的是positive look-behind

    (?<=abc)def
    

    编辑:

    既然您使用的是 JavaScript(抱歉,我只阅读了您的问题——我没有查看标签),为什么不直接使用常规捕获组并将匹配项包含在替换模式中?

    "abcdef".replace(/(abc)def/, "$1")
    

    【讨论】:

    • 当然,这将是 经典,但在 javascript 中没有后视。
    • 因为当替换为/g 时,它会跳过被替换的文本。例如"a1a2a".replace(/(a)(\d)(a)/g, "$1b$2b$3"); 将是ab1ba2a 而不是ab1bab2ba
    • @Zaffy - 仅将look-behind替换为捕获组,而不是look-ahead,例如:"a1a2a".replace(/(a)(\d)(?=a)/g, "$1b$2b") // =&gt; "ab1bab2ba"
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    • 1970-01-01
    • 2014-08-11
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 1970-01-01
    相关资源
    最近更新 更多