9.2 向前查找
向前查找指定了一个必须匹配但不在结果中返回的模式。向前查找实际就是一个子表达式,而且从格式上看也确实如此。从语法上看,一个向前查找模式其实就是一个以 ?=
开头的子表达式,需要匹配的文本跟在=的后面。
我们来看一个例子。例子里的原始文本是一些 URL 地址,而你的任务是把它们的协议名部分提取出来(为下一步处理做准备)。下面就是这个例子:
.+(?=:)
https://baidu.com http://aliyun.com https://qq.com
在上面列出的 URL 地址里,协议名与主机名之间以一个:分隔。模式.+匹配任意文本(第 1 个匹配是 http),子表达式 (?=:)
匹配:。注意,被匹配到的:并没有出现在最终的匹配结果里;我们用 ?=
向正则表达式引擎表明:只要找到:就行了,不要把它包括在最终的匹配结果里——用术语来说,就是“不消费”它。
为了更好地理解 ?=
的作用,我们再来看一个同样的例子,但这次不使用向前查找元字符:
.+(:)
https://baidu.com http://aliyun.com https://qq.com
子表达式 (:)
正确地匹配到了: 并消费了这个字符——: 出现了最终的匹配结果。
这两个例子的区别是前一个用来匹配:的模式是 (?=:)
,后一个用来匹配:的模式是 (:)
。这两个模式所匹配的东西是一样的——都是紧跟在协议名后面的那个:,它们之间的区别只是被匹配到的:字符有没有出现在最终的匹配结果里而已。在使用向前查找的时候,正则表达式分析器将向前查找并处理:匹配,但不会把它包括在最终的搜索结果里。模式 .+(:)
查找到并且匹配结果包含: 模式 .+(?=:)
查找到但匹配结果不包含。
向前查找(和向后查找)匹配本身其实是有返回结果的,只是这个结果的字节长度永远是 0 而已。因此,前后查找操作有时也被称为零宽度(zero-width)匹配操作。
任何一个子表达式都可以转换为一个向前查找表达式,只要给它加上一个 ?=
前缀即可。在同一个搜索模式里可以使用多个向前查找表达式,它们可以出现在模式里的任意位置(而不仅仅是出现在整个模式的开头——就像你们在上面看到的那样)。