Martin

正则表达式(二)

这篇文章主要是介绍了正则表达式中分支、分组、反向引用、贪婪匹配与非贪婪匹配、环视等知识点。


分支

分支 就是在正则表达式可以有多种匹配情况,在正则表达式(一)提到过在元字符[]内的字符称之为字符组如[awq],还有另一种表达式写法就是分支(a|w|q),那么分支符号就是|咯。

如果想匹配cooklook,这里可以写成[cl]ook,那么我想要匹配cooklookcaook,这时候字符组并不能满足要求([]只能匹配单个字符),那么就用分支形式(ca|c|l)ook。(分支可以是更复杂的表达式,而字符组的只能匹配单个字符,如果匹配单个字符的情况选择字符组,效率会高些)

我们在继续看复杂点的表达式,例如0\d{2}-\d{8} | 0\d{3}-\d{7} 此表达式意思就是可匹配三位区号,8位本地号(010-12345678)或者四位区号,7位本地号的号码。

另外要注意的是分支的条件顺序(从左到右,一旦匹配成功则不再继续匹配),例如\d{2}-\d{8}|\d{2}\d{2}|\d{2}-\d{8},第一个表达式可以匹配(23-12345678或者23),而第二个表达就只会匹配前两位(23),所以写分支时要注意条件顺序的位置。

分组

重复单个字符我们只需要在字符后面加上限定符,但如果想要重复多个字符该怎么办呢?
可以用小括号指定子表达式,重复子表达式的次数,也就是正则表达式中的分组

类别 语法 意义
捕获 (exp) 匹配exp,并捕获文本到自动命名组里
捕获 (?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
捕获 (?:exp) 匹配exp,不捕获文本,也不给此组分配组号
零宽断言 (?=exp) 匹配exp后面的位置
零宽断言 (?<=exp) 匹配exp前面的位置
零宽断言 (?!exp) 匹配后面跟的不是exp的位置
零宽断言 (?<!exp) 匹配前面跟的不是exp的位置
注释 (?#comment) 提供注释拥有阅读,并对表达式没有任何影响
  • 反向引用(用于重复匹配子表达式内容文本)

    (\d)\1{2}-(\d)\d\2-\1{3} 可匹配的结果是333-454-333(此表达式匹配结果的形式就是xxx-yzy-xxx),第一个小括号(\d)(捕获到的文本内容就分配给编号1的分组中),然后接是分组1捕获的内容(即\1也就是第一个小括号的内容)重复两次,接着就是-,再第二个小括号(\d)
    (捕获到的文本内容就分配给编号2的分组中),接着匹配\d,然后接是分组2捕获的内容(即\2) ,接着就是-,最后又重复三次分组1捕获的内容(即\1);

  • 环视 即匹配的是位置,而不会匹配内容

    顺序肯定环视(?=exp)

    \b\w+(?=ook\b),匹配以ook结尾的单词前面的部分(就是除了ook以外的部分),
    i am look 那么此字符串匹配到look中的l字母。((?=ook\b)就是匹配位置,而不会去匹配内容)

    逆序肯定环视(?<=exp)

    (?<=\bst)\w+\b,匹配以st开头的单词后面的部分(就是除了st以外的部分),
    staring abc,那么此字符串匹配到staring中的aring

    顺序否定环视(?!exp)

    在正则表达式(一)时候提到过反义,例如\b\w*a[^t]\w*\b,那么匹配中就是字母a后面不可以跟t字母,如ra这种是以a字母结尾是匹配不了的,因为[^t]是要匹配一个字符的(空格或其他),\w*\b可以匹配其他字符,那么就有ra string这种匹配情况。
    为了解决上面的情况,我们可以引用(?!exp),它只是匹配一个位置,不占有任何字符,所以表达式是\b\w*a(?!t)\w*\b

    逆序否定环视(?<!exp)

    (?<![a-z])\d{3},前面不可以小写字母的3位数,如A123,则匹配到123。

    再举个例子,如整个匹配文本不能出现字符串”abc”,这个正则表达式应该是^(?:(?!abc).)+$

    注意,括号匹配会捕获文本,如需不捕获可以用环视以及(?:)

    总体而言,环视相当于对”所在位置”附加一个条件,难点在于找到这个”位置”。

贪婪/懒惰匹配模式

当正则表达式中包含能接受重复的限定符时,通常的行为时(在使整个式能匹配的前提下)匹配尽可能多的字符,例如a.*b,如果用来搜索aabab,它会匹配整个字符串(即正则表达式中的贪婪匹配)
有时,我们需要匹配尽可能少的字符,即懒惰匹配,我们改变下前面表达式的限定符中加上?(即懒惰匹配模式),那么表达式就是a.*?b,同上用于搜索aabab,最先匹配到aabab这两组字符
既然是懒惰匹配为啥会先匹配到aab呢?简单地说表达式中有,比懒惰/贪婪匹配规则的优先级更高:最先开始的匹配是拥有最高优先权的

常用懒惰限定符如下表

懒惰限定符 意义
*? 重复0次或更多次,但尽可能的少重复
+? 重复1次或更多次,但尽可能的少重复
?? 重复0次或1次,但尽可能的少重复
{n,m}? 重复n次到m次 ,但尽可能的少重复
{n,}? 重复n次或更多次,但尽可能的少重复
1
2
3
4
5
<?php
$reg = "/<a[^>]+>(.*)<\/a>/";
$str = "<a href='http://baidu.com'>aaa</a><span>b</span><a herf='http://baidu.com'></a>";
preg_match_all($reg, $str, matches);
var_dump(matches);

运行上述的代码,结果是

1
2
3
4
5
6
7
8
9
10
11
12
array(2) {
[0]=>
array(1) {
[0]=>
string(79) "<a href='http://baidu.com'>aaa</a><span>b</span><a herf='http://baidu.com'></a>"
}
[1]=>
array(1) {
[0]=>
string(48) "aaa</a><span>b</span><a herf='http://baidu.com'>"
}
}

由于上述正则表达式中限定符.*会引发贪婪匹配模式,所以会一直匹配”<a herf='http://baidu.com'>“后的内容,直至遇到结束条件”<\/a>”,所以以至于匹配出我们不想要的结果,可以把表达式限定符加个?,/<a[^>]+>(.*?)<\/a>/,那么运行的结果也就是我们想要的结果啦(还可以把上述的表达式修改为/<a[^>]+>([^<>]*)<\/a>/也可以)