正则前向匹配和后向匹配

Last Modified: 2023/04/21

概述

有些时候无法直接匹配我们想要的内容,但是我们可以通过观察想要匹配内容的前面或者后面的内容来间接匹配真正想要匹配的内容。这就是我们今天想要分享的正则的表达式的前向匹配和后向匹配,或者称之为 lookahead 和 lookbehind。

正则前向匹配 lookahead

前向匹配要求待匹配内容的后面必须紧跟另外一个内容。前向匹配的正则表达形式为 Y(?=X),其中 Y 是我们希望匹配的内容,X 为 Y 必须紧跟的内容。举个例子来说,我们希望匹配的内容后面都必须跟紧 ing,则可以写成 (?=ing)

Pattern pattern = Pattern.compile("sb is (.*)(?=ing).+");
Matcher matcher = pattern .matcher("sb is walking");
Assertions.assertTrue(matcher.find());
Assertions.assertEquals(matcher.group(1), "walk");

有同学可能会说,使用 sb is (.*)ing.+ 不是一样的吗?确实,如果仅仅是这样,确实使不使用前向匹配都没啥区别,让我们看另外一个例子:

Pattern pattern = Pattern.compile("\\b\\w+\\b(?= great)");
Matcher matcher = pattern .matcher("With great power comes great responsibility.");
while(matcher.find()) {
  // 输出为 With 和 comes。
  System.out.println(matcher.group());
}

\\b 匹配的单词边界,以下是关于 \\b 更详细的解释:

\b is a zero-width assertion that matches if there is \w on one side, and either there is \W on the other or the position is beginning or end of string. \w is arbitrarily defined to be "identifier" characters (alnums and underscore), not as anything especially useful for English.

请仔细观察上面的代码,我们输出匹配结果时使用了 matcher.group(),但是输出结果中却并未包含 great。前向匹配还包含了另外一个功能:过滤掉不想匹配的内容。如果我们去掉前向匹配使用下面的正则表达式,输出结果将是 With greatcomes great

Pattern pattern = Pattern.compile("\\b\\w+\\b great");

正则负前向匹配 lookahead

理解了前向匹配 Lookahead 之后,那么负前向匹配(negative lookahead)就很好理解了,Lookahead 要求待匹配内容必须紧跟某个内容,negative lookahead 则要求待匹配内容必须不能紧跟某个内容,表达形式为 Y(?!X)

Pattern pattern = Pattern.compile("hello(?! kitty)");
Matcher matcher = pattern .matcher("hello kitty, hello siri, hello baby");
while(matcher.find()) {
  System.out.println(matcher.group());
}

上面的正则表达的含义是,要求 hello后面必须不能是 kitty,所以最后的输出结果只有两个 hello,而不是三个。

正则后向匹配 lookbehind

Lookbehind 要求待匹配内容的前面必须紧跟某个内容,表达形式为 (?<=X)Y,这里 Y 是我们想匹配的内容,要求 Y 的前面必须是 X。按照惯例,给个具体例子说明。假设一个需求,要求用正则匹配出 json 字符串中所有的 name 字段的值。

String a = "[{\"name\":\"lucy\",\"age\":10},{\"name\":\"lili\",\"age\":20}]";
Pattern pattern = Pattern.compile("(?<=\"name\":)\".*?\"");
Matcher matcher = pattern.matcher(a);
while(matcher.find()) {
  // 将会输出 "lucy" 和 "lili"
  System.out.println(matcher.group());
}

上面正则表达式的含义是匹配内容的前面必须是 "name":"

需要注意的是,lookbehind 在 Java 中是有限制,java8 包括 8 以前,(?<X)Y 中的 X 不能包含 *+,例如:

  • (?<a+)foo,表达的是 foo 的前面包含多个 a,这是不支持的,会抛出 PatternSyntaxException;
  • (?<a*)foo,表达的是 foo 的前面包含 0 或多个 a,也是不支持的;
  • (?a{1,}),这种没有上边界的同样不支持。当然也不是没有办法,只要限制一个上边界就可以了,例如:(?a{1,10})

正则负后向匹配 lookbehind

正则负后向匹配(Negative lookbehind)和 Lookbehind 正好相反,表达形式为 (?<!X)Y,这里 Y 是我们想匹配的内容,要求 Y 的前面必须不是 X

有问题吗?点此反馈!

温馨提示:反馈需要登录