正则表达式高级用法(分组与捕获)

1. 分组的引入

对于要重复单个字符,非常简单,直接在字符后卖弄加上限定符即可,例如

这些限定符如下所示:

公式 含义
X ? X ,一次或一次也没有
X * X ,零次或多次
X + X ,一次或多次
X { n } X ,恰好 n
X { n ,} X ,至少 n
X { n , m } X ,至少 n 次,但是不超过 m

但是我们如果要对多个字符进行重复怎么办呢?此时我们就要用到分组,我们可以使用小括号”()”来指定要重复的子表达式,然后对这个子表达式进行重复,例如:(abc)? 表示0个或1个abc 这里一 个括号的表达式就表示一个分组 。

分组可以分为两种形式,捕获组和非捕获组。

2. 捕获组

捕获组可以通过从左到右计算其开括号来编号 。例如,在表达式 (A)(B(C)) 中,存在四个这样的组:

分组号 捕获的文本
0 (A)(B(C))
1 (A)
2 (B(C))
3 (C)

组0 始终代表整个表达式

之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用(反向引用) 在表达式中使用,也可以在匹配操作完成后从匹配器检索。

2.1 Back 引用

Back 引用是说在后面的表达式中我们可以使用组的编号来引用前面的表达式所捕获到的文本序列。

注意:反向引用,引用的是前面捕获组中的文本而不是正则,也就是说反向引用处匹配的文本应和前面捕获组中的文本相同,这一点很重要。

【例】([“‘]).*\1

其中使用了分组,\1就是对引号这个分组的引用,它匹配包含在两个引号或者两个单引号中的所有字符串,如,”abc” 或 “ ‘ “ 或 ‘ “ ‘ ,但是请注意,它并不会对” a’或者 ‘a”匹配。原因上面已经说明,Back引用只是引用文本而不是表达式。

3. 非捕获组

以 (?) 开头的组是纯的非捕获 组,它不捕获文本 ,也不针对组合计进行计数。就是说,如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号,因此也不存在Back 引用。

我们通过捕获组就能够得到我们想要匹配的内容了,那为什么还要有非捕获组呢?原因是捕获组捕获的内容是被存储在内存中,可供以后使用,比如反向引用就是引用的内存中存储的捕获组中捕获的内容。而非捕获组则不会捕获文本,也不会将它匹配到的内容单独分组来放到内存中。所以,使用非捕获组较使用捕获组更节省内存。在实际情况中我们要酌情选用。

3.1 非捕获组(?:Pattern)

它的作用就是匹配Pattern字符,好处就是不捕获文本,不将匹配到的字符存储到内存中,从而节省内存。

【例】匹配indestry或者indestries

我们可以使用indestr(y|ies)或者indestr(?:y|ies)

【例】(?:a|A)123(?:b)可以匹配a123b或者A123b

非捕获组有很多种形式,其中包括:零宽度断言和模式修正符

3.2 零宽度断言

在使用正则表达式时,有时我们需要捕获的内容前后必须是特定内容,但又不捕获这些特定内容的时候,零宽断言就起到作用了。

公式 含义
(?=exp) 零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp
(?<=exp) 零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp
(?!exp) 零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp
(?<!exp) 零宽度负回顾后发断言,断言此位置的前面不能匹配表达式exp

这四个非捕获组用于匹配表达式X,但是不包含表达式的文本。

示例 文本 匹配
(?=_path) product_path product
(?<=name:) name:angelica angelica
(?!_path) product_name product
(?<!name:) nick_name:angelica angelica

3.3 模式修正符