代码之家  ›  专栏  ›  技术社区  ›  David Kanarek

regex lookahead命令

  •  6
  • David Kanarek  · 技术社区  · 15 年前

    我对正则表达式相当了解,现在我再次尝试理解lookahead和lookbehind断言。它们大多是有意义的,但我不太确定顺序是如何影响结果的。我一直在看 this site 它把lookbehind放在表达式之前,lookaheads放在表达式之后。我的问题是,这会改变什么吗?最近的一个回答把目光放在了导致我困惑的表情之前。

    3 回复  |  直到 15 年前
        1
  •  9
  •   Bart Kiers    15 年前

    当教程介绍查找时,他们倾向于为每个用例选择最简单的用例。所以他们会用这样的例子 (?<!a)b (‘B’前面没有‘A’)或 q(?=u) ('q'后接'u')。这只是为了避免把解释与分散注意力的细节混淆在一起,但它往往会造成(或强化)这样一种印象,即观察落后者和观察者应该按一定的顺序出现。我花了很长时间才理解这个想法,我也看到其他几个人也为此苦恼。

    试着看一些更现实的例子。一个经常出现的问题涉及验证密码;例如,确保新密码至少有六个字符长,并且包含至少一个字母和一个数字。一种方法是:

    ^(?=.*[A-Za-z])(?=.*\d)[A-Za-z0-9]{6,}$
    

    角色类 [A-Za-z0-9]{6,} 可以匹配所有字母或所有数字,因此使用lookaheads确保每个字母或数字中至少有一个。在这种情况下,你必须做表头 第一 ,因为regex的后面部分必须能够检查整个字符串。

    对于另一个例子,假设您需要查找单词“there”的所有出现位置,除非它前面有引号。最明显的例子是 (?<!")[Tt]here\b 但是,如果您搜索的是大型语料库,这可能会造成性能问题。如前所述,regex将对文本中的每个位置进行反向查找,并且只有成功后,它才会检查regex的其余部分。

    每一个regex引擎都有其自身的优点和缺点,但有一点是对所有引擎都是正确的,那就是它们比任何其他引擎都更快地找到文字字符的固定序列——序列越长越好。这意味着它可以大大更快地进行向后看。 最后的 ,即使它意味着将单词匹配两次:

    [Tt]here\b(?<!"[Tt]here)
    

    因此,控制了望台位置的规则是没有规则;你把它们放在任何它们在每种情况下最有意义的地方。

        2
  •  4
  •   Max Shawabkeh    15 年前

    我认为,以身作则比解释更容易。让我们来看看这个regex:

    (?<=\d)(?=(.)\1)(?!p)\w(?<!q)
    

    这意味着:

    1. (?<=\d) -确保在匹配位置之前出现的是数字。
    2. (?=(.)\1) -确保在这个(相同)位置匹配的任何字符后面都有一个自身的副本(通过backreference)。
    3. (?!p) -确保以下内容不是 p .
    4. \w -匹配字母、数字或下划线。请注意,这是我们第一次实际匹配和使用角色。
    5. (?<!q) -确保我们到目前为止的匹配不会以 q .

    所有这些都将匹配字符串 abc5ddx 9xx 但不是 5d 6qq asd6pp add . 请注意,每个断言都是独立工作的。它只是停下来,环顾四周,如果一切正常,就允许匹配继续进行。

    还要注意,在大多数(可能全部)实现中,lookbehind具有固定长度的限制。不能使用重复/可选性运算符,例如 ? , * + 在他们里面。这是因为为了匹配一个模式,我们需要一个起点,否则我们必须尝试从字符串中的每个点匹配每个lookback。

    在字符串上运行此regex的示例 a3b5ddx 如下:

    1. 文本光标位置:0。
      1. 尝试匹配位置-1的第一个后视镜(自 \d 始终匹配1个字符)。我们不能匹配负索引,所以失败并前进光标。
    2. 文本光标位置:1。
      1. 尝试匹配位置0的第一个lookback。 a 不匹配 D 所以失败并再次前进光标。
    3. 文本光标位置:2。
      1. 尝试匹配位置1的第一个后视镜。 3 确实匹配 D 因此,保持光标完整并继续匹配。
      2. 尝试匹配位置2的第一个先行。 b 比赛 (.) 并被捕获。 5 不匹配 \1 (被俘者是谁) )因此,失败并前进光标。
    4. 文本光标位置:3。
      1. 尝试匹配位置2的第一个后视镜。 不匹配 \ D 所以失败并再次前进光标。
    5. 文本光标位置:4。
      1. 尝试匹配位置3的第一个后视镜。 5个 确实匹配 D 因此,保持光标完整并继续匹配。
      2. 试着匹配第4个位置的第一个先行。 d 比赛 () 并被捕获。第二 D 是否匹配 1 (第一次被抓获 D )允许匹配从我们停止的位置继续。
      3. 尝试匹配第二个展望。 位置4不匹配 因为这是一个负面的前瞻,所以这就是我们想要的;允许匹配继续进行。
      4. 试配 \w 位置4。 比赛。前进光标,因为我们已经消耗了一个字符并继续。同时将此标记为比赛的开始。
    6. 文本光标位置:5。
      1. 试着匹配第二个向后看的位置4(因为 Q 始终匹配1个字符)。 D 不匹配 Q 这就是我们要从消极的背后看出来的。
      2. 意识到我们在regex的末尾,并通过将子字符串从匹配的开始返回到当前位置(4到5)来报告成功,即 D .
        3
  •  1
  •   Community CDub    7 年前

    1(?=ABC) 手段-寻找 1 和匹配(但不捕获) ABC 之后。
    (?<=ABC)1 方法-匹配(但不捕获) 基础知识 在当前位置之前,并继续匹配 .
    所以,通常情况下,您会将lookahead放在表达式之后,而lookback放在表达式之前。

    当我们在表情后面看的时候,我们 重新检查我们已经匹配的字符串 . 当你有复杂的情况时,这是很常见的(你可以把它看作 AND ReEXS的)。例如,通过以下方式查看最近的答案: Daniel Brückner :

    .&.(?<! & )
    

    首先,捕获两个字符之间的和号。接下来,你检查它们都不是空格( \S&\S 不会在这里工作,操作人员想要捕捉 1&_ )