正则表达式小记
正则表达式日常基础的可以达到看懂能写的地步,但是一直对以下三个点不大清楚,今天得恶补记录下啦。
在介绍以下三点前,先将常用分组语法列出如下:
分类 | 代码/语法 | 说明 |
---|---|---|
捕获 | (exp) | 匹配exp,并捕获文本到自动命名的组里 |
?< name >exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp) | |
(?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 | |
零宽断言 | (?=exp) | 匹配exp前面的位置 |
(?<=exp) | 匹配exp后面的位置 | |
(?! exp) | 匹配后面跟的不是exp的位置 | |
?(exp) | 匹配前面不是exp的位置 |
一、后向引用
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1
代表分组1匹配的文本
\b(\w+)\b\s+\1\b
可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b)
,这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+)
,最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)
。
二、环视
环视这个名词叫法很多,有些书上会叫做预搜索,也有些书上会叫做零宽断言。其实表达的是同一个意思。
环视按照方向划分有顺序和逆序两种,按照是否匹配有肯定和否定两种,组合起来就有四种环视 (?<=Expression) 逆序肯定环视,表示所在位置左侧能够匹配Expression (?<!Expression) 逆序否定环视,表示所在位置左侧不能匹配Expression (?=Expression) 顺序肯定环视,表示所在位置右侧能够匹配Expression (?!Expression) 顺序否定环视,表示所在位置右侧不能匹配Expression
举例如下: 源字符串:
aa<p>one</p>bb<div>two</div>cc
0 2 8 14 18 30
正则表达式:<(?!/?p\b)[^>]+>
- 首先由字符“<”取得控制权,从位置0开始匹配,由于“<”匹配“a”失败,在位置0处整个表达式匹配失败,第一次迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。
- 重复以上过程,直到位置2,“<”匹配“<”成功,控制权交给
“(?!/?p\b)”
。 “(?!/?p\b)”
子表达式取得控制权后,进行内部子表达式的匹配。首先由“/?”取得控制权,尝试匹配“/”失败,进行回溯,不匹配,控制权交给“p”;- 由“p”来尝试匹配“p”,匹配成功,控制权交给“\b”
- 由“\b”来尝试匹配位置4,匹配成功。此时子表达式匹配完成,“/?p\b”匹配成功,那么环视表达式
“(?!/?p\b)”
就匹配失败 - 在位置2处整个表达式匹配失败,新一轮迭代匹配失败,正则引擎向前传动,由位置3处开始尝试下一轮迭代匹配。
- 在位置8处也会遇到一轮
“/?p\b”
匹配“/p”
成功,而导致环视表达式“(?!/?p\b)”
匹配失败,从而导致整个表达式匹配失败的过程。 - 重复以上过程,直到位置14,“<”匹配“<”成功,控制权交给
“(?!/?p\b)”
; “/?”
尝试匹配“d”失败,进行回溯,不匹配,控制权交给“p”;由“p”来尝试匹配“d”,匹配失败,已经没有备选状态可供回溯,匹配失败。此时子表达式匹配完成,“/?p\b”
匹配失败,那么环视表达式“(?!/?p\b)”
就匹配成功- 匹配的结果是位置15,然后控制权交给
“[^>]+”
;由“[^>]+”
从位置15进行尝试匹配,可以成功匹配到“div”
,控制权交给“>”
;由“>”
来匹配“>”
。
以上的例子是一个 环视顺序否定匹配。逆序否定一般用的较少。简单了解即可。
三、贪婪/懒惰匹配
贪婪是指 尽可能多的去匹配。 懒惰是指 尽可能少的去匹配。
分类 | 代码/语法 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复1次或更多次,但尽可能少重复 |
?? | 重复0次或1次,但尽可能少重复 |
{n,m}? | 重复n到m次,但尽可能少重复 |
{n,}? | 重复n次以上,但尽可能少重复 |