最大最小匹配、零宽断言、反向引用、非获取匹配等

最大、最小匹配

正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符,例如:

<.*> 匹配 <h1>People's Daily Online</h1>

匹配结果

共找到 1 处匹配:
<h1>People's Daily Online</h1>

当匹配到 h1 后面的 > 后,还会继续尝试往后匹配,直到匹配到最后一个 >(如果存在的话),若只想匹配到第一个 > 就停止匹配的话,即匹配尖括号里 h1 或 /h1 的话,需设定为最小匹配,方法是在限定符后面加个 ?

<.*><.*?>

匹配结果

共找到 2 处匹配:
<h1>
</h1>

再举个例子

a.*b 匹配 aabbccbb,匹配结果

共找到 1 处匹配:
aabbccbb

a.*?b 匹配 aabbccbb,匹配结果

共找到 1 处匹配:
aab

总结如下:

代码/语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

零宽断言

零宽断言是一种零宽度的匹配,它匹配到的内容不会保存到匹配结果中去。简单来说就是给表达式添加一个限定条件,规定表达式前面或者后面需要满足某种条件。

(?=Exp): 后面匹配Exp

(?!Exp): 后面不匹配Exp

(?<=Exp): 前面匹配Exp

(?<!Exp): 前面不匹配Exp

举例字符串 AbcabC

(?<=[A-Z])b 匹配

共找到 1 处匹配:
b

b(?=[A-Z]) 匹配

共找到 1 处匹配:
b

反向引用

捕获组(圆括号里的表达式)捕获到的内容在正则表达式内部进行引用

aabbcccdd 中,如果想匹配连续三个一样的字母,就用到了反向引用

([a-z])\1{2} 匹配,结果

共找到 1 处匹配:
ccc

来拆解一下这个正则

([a-z]) 是第一个捕获组

\1 表示引用了第1个捕获组

{2} 表示 \1 重复两次

过程是一开始匹配到了 a时, \1 就变成了 a, 然后去匹配 aaa 失败,b 同理,最后只有 ccc 符合

捕获组还可以进行命名,命名规则是 (?<name>) ,使用的时候\k<name>

那么可以给 ([a-z])\1{2} 命名成 (?<char>[a-z])\k<char>{2}

非获取匹配

因为小括号()会被当做捕获组,有些情况下我们其实不需要它被捕获,则需要添加 ?:

(?:[a-z])\1{2} 匹配 aabbcccdd,结果

(没有匹配)

在括号内加了 ?: 以后,\1 就没有可以引用的了


常用正则表达式

场景 正则
十六进制值 ^#?([a-f0-9]{6}|[a-f0-9]{3})$
电子邮箱 ^([a-z0-9_.-]+)@([\da-z.-]+).([a-z.]{2,6})$/ /^[a-z\d]+(.[a-z\d]+)*@(\da-z?)+(.{1,2}[a-z]+)+$
URL ^(https?:\/\/)?([\da-z.-]+).([a-z.]{2,6})([\/\w .-])\/?$
IP 地址 ((2[0-4]\d|25[0-5]|[01]?\d\d?).){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
Unicode编码中的汉字范围 ^[\u2E80-\u9FFF]+$