2010-01-24 76 views
6

我对正则表达式相当不错,现在我再次尝试理解前瞻和后向断言。他们大多数都有道理,但我不太清楚订单如何影响结果。我一直在寻找this site,它在表达式之前放置向后看,并且在表达式之后向前看。我的问题是,这是否会改变什么?最近在SO上的一个回答把这个前瞻放在导致我混乱的表达之前。正则表达式顺序

回答

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,但是如果您搜索的是大型语料库,那可能会造成性能问题。正如所写的那样,该正则表达式将在文本中的每一个位置都做负面的后台关系,并且只有当它成功时才会检查正则表达式的其余部分。

每一个正则表达式引擎都有自己的长处和短处,但其中的一点是,它们更快地找到文字字符的固定序列,而不是其他任何东西 - 序列越长越好。这意味着它可以大大加快做回顾后最后,即使这意味着这个词两次匹配:

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

所以管理lookarounds的位置,规则是没有规则;你把它们放在最有意义的地方。

1

1(?=ABC)表示 - 查找1,并且匹配(但不捕获)后面的ABC
(?<=ABC)1表示在当前位置之前匹配(但不捕获)ABC,并继续匹配1
因此,通常情况下,您会在前面的表达式和后面的后面放置向前看。

当我们在表达式后面放置一个倒序时,我们正在重新检查我们已经匹配的字符串。当你有复杂的条件时,这很常见(你可以把它想象成AND的正则表达式)。例如,拿在最近这次的回答看看由Daniel Brückner

.&.(?<! &) 

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

+0

'[^]&[^]'可能比'。&。(?<!&)'更容易理解。 – Gumbo 2010-01-24 09:30:12

+2

这将不会匹配“this&that”,而lookbehind版本会。一个有效的等价物应该是:'\ S&|&\ S' – 2010-01-24 09:47:09

4

我觉得在示例中比解释更容易。让我们这个表达式:

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

这意味着:

  1. (?<=\d) - 确保赛前位置是一个数字会发生什么。
  2. (?=(.)\1) - 确保我们在这个(相同)位置匹配的任何字符后面跟着一个自身副本(通过反向引用)。
  3. (?!p) - 确保接下来不是p
  4. \w - 匹配字母,数字或下划线。请注意,这是我们第一次真正匹配并使用角色。
  5. (?<!q) - 确保我们迄今为止匹配的内容不会以q结尾。

所有这一切都将匹配像abc5ddx9xx但不5d6qqasd6ppadd字符串。请注意,每个断言独立工作。它只是停下来,环顾四周,如果一切顺利的话,让匹配继续下去。

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

此正则表达式的串a3b5ddx的样品运行如下:

  1. 文本光标位置:0
    1. 尝试第一回顾后在位置匹配-1(因为\d总是匹配1个字符)。我们不能匹配负指数,所以失败并提前光标。
  2. 文本光标位置:1.
    1. 尝试匹配位置0 a第一回顾后不匹配\d如此失败,再次向前移动光标。
  3. 文本光标位置:2
    1. 尝试在第一回顾后在位置1 3匹配不匹配\d所以保持光标不变,并且继续匹配。
    2. 尝试匹配位置2处的第一个预测。b匹配(.)并被捕获。 5不匹配\1(这是被捕获的b)。因此,失败并推进游标。
  4. 文本光标位置:3
    1. 尝试匹配在2位b第一回顾后不匹配\d如此失败,再次向前移动光标。
  5. 文本光标位置:4
    1. 尝试在第一回顾后在位置3 5匹配不匹配\d所以保持光标不变,并且继续匹配。
    2. 尝试匹配位置4处的第一个预测。d匹配(.)并被捕获。第二个d确实匹配\1(这是第一个捕获的d)。允许匹配从我们离开的地方继续。
    3. 尝试匹配第二个预测。在位置4处的bp不匹配,并且由于这是负向前视,所以我们想要;允许匹配继续。
    4. 尝试匹配\w的第4位。b比赛。由于我们已经消耗了一个字符并继续,因此前进光标同样将此标记为比赛的开始。
  6. 文本光标位置:5.
    1. 尝试匹配在第4位的第二回顾后(因为q总是匹配1个字符)。 d不符合q这是我们想要从负面看后面。
    2. 意识到我们处于正则表达式的末尾,并通过将匹配字符串从当前位置(4到5)返回到d来报告成功。