跳到主要内容

10.2 正则表达式里的条件

正则表达式里的条件要用 ? 来定义。事实上,你们已经见过几种非常特定的条件了:

  • ? 匹配前一个字符或表达式——如果它存在的话。
  • ?=?<= 匹配前面或后面的文本——如果它存在的话。

嵌入条件语法也使用了?,这并没有什么让人感到吃惊的地方——因为嵌入条件不外乎以下两种情况:

  • 根据一个回溯引用来进行条件处理。
  • 根据一个前后查找来进行条件处理。

10.2.1 回溯引用条件

回溯引用条件只在一个前面的子表达式搜索取得成功的情况下才允许使用一个表达式。听起来很费解,我们还是用一个例子来说明好了:你需要把一段文本里的图标全都找出来;不仅如此,如果某个图是一个链接(被括在<a></a>标签之间)的话,你还要把整个链接标签匹配出来。

请看下面这个例子:

(<a\s+>\s*)?<i class="icon icon-(.*?)"/>(?(1)\s*</a>)
<ol>
  <li><a href="/"><i class="icon icon-home"/></a></li>
  <li><a href="/books"><i class="icon icon-books"/></a></li>
  <li><i class="icon icon-tutorials"/></li>
  <li><i class="icon icon-rss"/></li>
</ol>
警告

Javascript 正则表达式引擎不支持条件特性,PHP 等编程语言是支持的。

<?php

$text = '
<ol>
<li><a href="/"><i class="icon icon-home"/></a></li>
<li><a href="/books"><i class="icon icon-books"/></a></li>
<li><i class="icon icon-tutorials"/></li>
<li><i class="icon icon-rss"/></li>
</ol>
';

$matches = [];

// 使用子表达式编号回溯引用
preg_match_all('/(<a\s+[^>]+>\s*)?<i class="icon icon-(\w*?)"\/>(?(1)\s*<\/a>)/', $text, $matches);

// 使用命名捕获回溯引用
// preg_match_all('/(?<tag><a\s+[^>]+>\s*)?<i class="icon icon-(\w*?)"\/>(?(tag)\s*<\/a>)/', $text, $matches);

var_dump($matches);

10.2.2 前后查找条件

前后查找条件只在一个向前查找或向后查找操作取得成功的情况下才允许一个表达式被使用。定义一个前后查找条件的语言与定义一个回溯引用条件语法大同小异——只需要把回溯引用替换成一个完整的前后查找表达式就行了。

下面是一个 java 类列表,包括单个类和子类,下面是尝试匹配来每个合法的类名称

([a-z_]\w?)+(\$([a-z_]\w?)+)?
在工具中查看
Animal
Animal$Dog
Food$

我们成功的把 Animal 和 它的 Dog 子类匹配了出来,但 Food$ 并不是一个合法的类(它后面有$但没有跟子类名)也被匹配了出来。如果我们只想要匹配合法的类名要怎么做呢?下面是一种可用的方法:

\b([a-z_]\w?)+\b(?(?=\$)\$\b([a-z_]\w?)+\b)
在工具中查看
Animal
Animal$Dog
Food$

这次的模式添加一个向前查找 (?=\$),只有向前查找条件满足时才匹配后面的$和子类名称。

下面是 PHP 代码,可以直接保存为 .php 文件后运行

<?php

$text = '
Animal
Animal$Dog
Food$
';

$matches = [];

preg_match_all('/\b([a-z_]\w?)+\b(?(?=\$)\$\b([a-z_]\w?)+\b)/i',$text,$matches);

var_dump($matches);
该内容基于 《正则表达式必知必会》 二度创作
转载请遵守原作者相关协议并注明本页地址
https://iamhefang.cn/tutorials/正则表达式/第十章-嵌入条件/10.2-正则表达式里的条件