技术 · 阅读记录

正则表达式中的断言

一、介绍

断言(Assertion),也被称为零宽度断言(zero-width assertions),本身不匹配字符,而是匹配字符串中的位置。

断言又包含锚位符(Anchor)、环视(Lookarounds)。

  • 锚位符,也被称为边界(Boundary),
  • 环视,是一种非捕获分组,它根据某个模式之前或之后的内容匹配其他模式。

二、边界

Anchor Description
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾

三、环视

环视(lookaround)是一种非捕获分组,它根据某个模式之前或之后的内容匹配其他模式。环视也被称为零宽度断言(zero-length assertions)。

Lookaround Description
(?=X) X, via zero-width positive lookahead (正前瞻)
(?!X) X, via zero-width negative lookahead (反前瞻)
(?<=X) X, via zero-width positive lookbehind (正后顾)
(?<!X) X, via zero-width negative lookbehind (反后顾)

环视包含如下四种:

  • 正前瞻(positive lookahead)
  • 反前瞻(negative lookahead)
  • 正后顾(positive lookbehind)
  • 反后顾(negative lookbehind)

在实际的用例中,使用到如下帮组方法:

1
2
3
    private static boolean canMatch(String patternString, String string) {
        return Pattern.compile(patternString).matcher(string).find();
    }

3.1 正前瞻

(?=X) X, via zero-width positive lookahead (正前瞻)

测试代码如下:

1
2
3
4
5
    @Test
    public void testPositiveLookahead() {
        Assert.assertTrue(canMatch("Timeout(?=Exception)", "TimeoutException"));
        Assert.assertFalse(canMatch("Timeout(?=Exception)", "Timeout exception"));
    }

3.2 反前瞻

(?!X) X, via zero-width negative lookahead (反前瞻)

测试代码如下:

1
2
3
4
5
    @Test
    public void testNegativeLookahead() {
        Assert.assertTrue(canMatch("Timeout(?!Exception)", "Timeout exception"));
        Assert.assertFalse(canMatch("Timeout(?!Exception)", "TimeoutException"));
    }

3.3 正后顾

(?<=X) X, via zero-width positive lookbehind (正后顾)

测试代码如下:

1
2
3
4
5
    @Test
    public void testPositiveLookbehind() {
        Assert.assertTrue(canMatch("(?<=Timeout)Exception", "TimeoutException"));
        Assert.assertFalse(canMatch("(?<=Timeout)Exception", "NullPointerException"));
    }

3.4 反后顾

(?<!X) X, via zero-width negative lookbehind (反后顾)

测试代码如下:

1
2
3
4
5
    @Test
    public void testNegativeLookbehind() {
        Assert.assertTrue(canMatch("(?<!Timeout)Exception", "NullPointerException"));
        Assert.assertFalse(canMatch("(?<!Timeout)Exception", "TimeoutException"));
    }

四、应用

场景一

在做线上 log 查看时,由于大量超时 log,这边做搜索时只是搜索常见异常,其模式为\java.lang.*Exception。今天上线,遇到了 Json 解析失败异常,查询原因为上游新老版本逻辑有点不一致导致的。反思这很不应该,故尝试使用断言来查询除了超时异常之前的其他异常。

场景二

密码强度匹配

五、总结