正则表达式小记

    2015年10月13日 doc test 字数:2780

正则表达式小记


正则表达式日常基础的可以达到看懂能写的地步,但是一直对以下三个点不大清楚,今天得恶补记录下啦。

在介绍以下三点前,先将常用分组语法列出如下:

分类 代码/语法 说明
捕获 (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)[^>]+>

  1. 首先由字符“<”取得控制权,从位置0开始匹配,由于“<”匹配“a”失败,在位置0处整个表达式匹配失败,第一次迭代匹配失败,正则引擎向前传动,由位置1处开始尝试第二次迭代匹配。
  2. 重复以上过程,直到位置2,“<”匹配“<”成功,控制权交给“(?!/?p\b)”
  3. “(?!/?p\b)”子表达式取得控制权后,进行内部子表达式的匹配。首先由“/?”取得控制权,尝试匹配“/”失败,进行回溯,不匹配,控制权交给“p”;
  4. 由“p”来尝试匹配“p”,匹配成功,控制权交给“\b”
  5. 由“\b”来尝试匹配位置4,匹配成功。此时子表达式匹配完成,“/?p\b”匹配成功,那么环视表达式“(?!/?p\b)”就匹配失败
  6. 在位置2处整个表达式匹配失败,新一轮迭代匹配失败,正则引擎向前传动,由位置3处开始尝试下一轮迭代匹配。
  7. 在位置8处也会遇到一轮“/?p\b”匹配“/p”成功,而导致环视表达式“(?!/?p\b)”匹配失败,从而导致整个表达式匹配失败的过程。
  8. 重复以上过程,直到位置14,“<”匹配“<”成功,控制权交给“(?!/?p\b)”
  9. “/?”尝试匹配“d”失败,进行回溯,不匹配,控制权交给“p”;由“p”来尝试匹配“d”,匹配失败,已经没有备选状态可供回溯,匹配失败。此时子表达式匹配完成,“/?p\b”匹配失败,那么环视表达式“(?!/?p\b)”就匹配成功
  10. 匹配的结果是位置15,然后控制权交给“[^>]+”;由“[^>]+”从位置15进行尝试匹配,可以成功匹配到“div”,控制权交给“>”;由“>”来匹配“>”

以上的例子是一个 环视顺序否定匹配。逆序否定一般用的较少。简单了解即可。

三、贪婪/懒惰匹配

贪婪是指 尽可能多的去匹配。 懒惰是指 尽可能少的去匹配。

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