正则表达式预测

时间:2010-01-24 06:03:22

标签: regex

我对正则表达式非常体面,现在我再次尝试了解前瞻和后瞻性断言。它们大多有意义,但我不太确定订单如何影响结果。我一直在看this site,它在表达式之前放置了lookbehinds,并在表达式之后进行了预测。我的问题是,这会改变什么吗?最近关于SO的答案在表达之前放置了前瞻,这导致了我的困惑。

3 个答案:

答案 0 :(得分:9)

当教程介绍了lookarounds时,他们倾向于为每个用例选择最简单的用例。因此,他们会使用(?<!a)b('b'前面没有'a')或q(?=u)('q'后跟'u')等示例。这只是为了避免使用令人分心的细节混淆解释,但它往往会产生(或强化)外观和前瞻应该以某种顺序出现的印象。我花了很长时间来克服这个想法,而且我也见过其他几个受其影响的人。

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

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

字符类[A-Za-z0-9]{6,}可以匹配所有字母或所有数字,因此您使用前瞻以确保每个字符中至少有一个。在这种情况下,你必须首先进行前瞻 ,因为正则表达式的后面部分必须能够检查整个字符串。

再举一个例子,假设你需要找到所有出现的单词“there”,除非它前面带有引号。显而易见的正则表达式是(?<!")[Tt]here\b,但如果您正在搜索大型语料库,则可能会产生性能问题。如上所述,正则表达式将在文本中的每个位置执行负面的lookbehind,并且只有在成功时才会检查正则表达式的其余部分。

每个正则表达式引擎都有自己的优点和缺点,但有一点是真实的,就是它们能够更快地找到固定的文字字符序列而不是其他任何东西 - 序列越长越好。这意味着可以更快地执行lookbehind last ,即使这意味着匹配单词两次:

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

因此,规定外观的规则是没有规则;你把它们放在每种情况下最有意义的地方。

答案 1 :(得分:4)

我认为,在一个例子中展示比解释更容易。我们来看看这个正则表达式:

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

这意味着:

  1. (?<=\d) - 确保匹配位置前的数字为数字。
  2. (?=(.)\1) - 确保我们在此(相同)位置匹配的任何字符后跟其自身的副本(通过反向引用)。
  3. (?!p) - 确保以下内容不是p
  4. \w - 匹配字母,数字或下划线。请注意,这是我们第一次实际匹配并使用该角色。
  5. (?<!q) - 确保我们目前匹配的内容不会以q结尾。
  6. 所有这些都会匹配abc5ddx9xx但不是5d6qqasd6ppadd等字符串。请注意,每个断言都是独立工作的。它只是停下来,环顾四周,如果一切顺利,就可以继续匹配。

    另请注意,在大多数(可能是所有)实现中,lookbehinds具有固定长度的限制。您不能在其中使用?*+等重复/可选操作符。这是因为要匹配一个模式,我们需要一个起点 - 否则我们必须尝试匹配字符串中每个点的每个lookbehind。

    此正则表达式在字符串a3b5ddx上的示例运行如下:

    1. 文本光标位置:0。
      1. 尝试匹配位置-1处的第一个lookbehind(因为\d总是匹配1个字符)。我们无法匹配负指数,因此失败并推进光标。
    2. 文本光标位置:1。
      1. 尝试匹配位置0的第一个lookbehind。a\d不匹配,因此失败并再次前进光标。
    3. 文本光标位置:2。
      1. 尝试匹配位置1的第一个lookbehind。3匹配\d,因此请保持光标不变并继续匹配。
      2. 尝试匹配位置2的第一个前瞻。b匹配(.)并被捕获。 5\1(已捕获的b)不匹配。因此,失败并前进光标。
    4. 文本光标位置:3。
      1. 尝试匹配位置2的第一个lookbehind。b\d不匹配,因此失败并再次前进光标。
    5. 文本光标位置:4。
      1. 尝试匹配位置3的第一个lookbehind。5匹配\d,因此请保持光标不变并继续匹配。
      2. 尝试匹配位置4的第一个前瞻。d匹配(.)并被捕获。第二个d\1匹配(这是第一个被捕获的d)。允许匹配从我们中断的地方继续。
      3. 尝试匹配第二个前瞻。位置4的bp不匹配,因为这是一个负面的预测,这就是我们想要的;允许匹配继续。
      4. 尝试匹配位置4的\w b匹配。因为我们已经消耗了一个角色并继同时将此标记为比赛的开始。
    6. 文本光标位置:5。
      1. 尝试匹配位置4的第二个lookbehind(因为q总是匹配1个字符)。 dq不匹配,这是我们想要的负面看法。
      2. 通过将匹配开头的子字符串返回到当前位置(4到5)(即d),意识到我们正处于正则表达式的末尾并报告成功。

答案 2 :(得分:1)

1(?=ABC)表示 - 查找1,然后匹配(但不捕获)ABC
(?<=ABC)1表示 - 在当前位置之前匹配(但不捕获)ABC,并继续匹配1
所以,通常情况下,你会在表达式之后放置前瞻,并将它放在它之前。

当我们在表达式后面放置一个lookbehind时,我们重新检查我们已匹配的字符串。当您有复杂的条件时(通常可以将其视为正则表达式的AND),这种情况很常见。例如,请查看Daniel Brückner最近的答案:

.&.(?<! & )

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