9.4 对前后查找取非
到目前为止正如你看到的那样,向前查找和向后查找通常用来匹配文本,其目的是为了确定将被返回为匹配结果的文本的位置(通过指定匹配结果的前后必须是哪些文本)。这种用法被称为正向前查找(positive lookahead)和正向后查找(positive lookbehind)。术语“正”指的是寻找匹配的事实。
前后查找还有一种不太常见的用法叫做负前后查找(negative lookaround)。负向前查找(negative lookahead)将向前查找不与给定模式相匹配的文本,负向后查找(negative lookbehind)将向后查找不与给定模式相匹配的文本。
我们在第 3 章曾经介绍过一个用来对字符集合进行取非处理的操作符 ^
,但 ^
不能用来对前后查找进行取非处理。这里必须使用另外一种语法:前后查找必须用 !
来取非(它将替换掉 =
)。下表列出了所有的前后查找操作符。
操作符 | 说明 |
---|---|
(?=) | 正向前查找 |
(?!) | 负向前查找 |
(?<=) | 正向后查找 |
(?<!) | 负向后查找 |
一般来说,凡是支持向前查找的正则表达式实现都同时支持正向前查找和负向前查找。类似地,凡是支持向后查找的正则表达式实现都同时支持正向后查找和负向后查找。
为了演示正向查找和负向查找的区别,我们来看一个例子。下面是一段包含数字的文本,里面既有价格又有数量,我们先来查找且只查找价格:
(?<=¥)\d+
我花了 ¥100 ,买了 20 个苹果,10 个橘子和 10 个香蕉,便宜了 ¥5。
这个例子里面我们使用向后查找来查找价格,(?<=¥)
匹配"¥",但是不消费它。\d+
匹配数字并消费它。这样就可以正确匹配到价格,但不会匹配到同样是数字的数量。
下面,我们再来查找且只查找数量:
\b(?<!¥)\d+\b
我花了 ¥100 ,买了 20 个苹果,10 个橘子和 10 个香蕉,便宜了 ¥5。
\d+
还是匹配数值,但这次只匹配数量,不匹配价格。表达式 (?<!¥)
是一个负向后查找,它使得最终的匹配结果只包含那些不以"¥"开头的数值。把操作符 ?<=
改为操作符 ?<!
使得整个模式从一个正向后查找变成了一个负向后查找。
在上面这个例子里,我们还在那个负向后查找模式里用 \b
元字符定义了两个单词边界。我们为什么要那么做呢?你看过下面这个没有使用单词边界的例子里就明白了。
(?<!¥)\d+
我花了 ¥100 ,买了 20 个苹果,10 个橘子和 10 个香蕉,便宜了 ¥5。
我们可以看到,因为没有使用单词边界,"¥100" 里面的 "00" 也出现在了结果里。这是因为第 1 个 0 前面是一个数字 1 而不是 ¥。它也符合 (?<!¥)\d+
。用单词边界括进来从根本上解决了这个问题。