正则表达式——捕获组与非捕获组

1. 基本概念

正则表达式是一个由一些普通字符和一些元字符组成的字符串,这个字符串在正则引擎的作用下,可以用来匹配字符串。

正则匹配的流程与两个字符串严格匹配的过程类似。假设它们为 str, pattern,匹配的大致流程如下:

  1. str 和 pattern 左对齐
  2. 判断是否匹配
  3. 如果匹配,匹配长度为 len, 则 pattern 向右滑动 len 个单位,重复 2
  4. 如果不匹配,pattern 向右滑动 1 个单位,重复 2
  5. 匹配时出现下标越界或滑动时出现下标越界则匹配结束

理解这个流程对于我们理解后面的内容有很大的帮助,记住一点,正则表达式是沿字符串不断向右滑动的,匹配的时候也是从最左往右匹配。

2. 捕获组 ( )

在正则表达式的某一子表达式两边加上一对括号,可以创建一个捕获组,用于获取匹配该子表达式的字符串。

这种用法非常常见,因为大多数时候一短文本里只有一小部分是我们需要的信息,但是我们又需要靠一些额外的信息来定位我们需要的信息,这时我们写的正则表达式匹配到的数据就分为了两部分,一部分是需要的,一部分是不需要的。为了只获取需要的信息,我们可以为这个信息创建一个捕获组。

举个栗子栗子:

username=hehe
password=haha

如果我们想解析上面文本中 username 字段对应的值,可以使用下面这个正则表达式

username=(\w+)

如果我们使用 username=\w+ ,匹配得到的字符串是 username=hehe

在正则里加入一个括号,就创建了一个匹配组,我们可以通过这个匹配组的编号来获取匹配组里匹配到的内容。username=(\w)+ 匹配得到的字符串也是 username=hehe, 但它还有一个编号为 1 的匹配组,其内容为 hehe

再看一个例子:
string: "abc"
pattern: "(a)(b)(c)"

使用 (a)(b)(c) 作为 pattern 来匹配字符串 "abc" 的时候会产生三个捕获组,编号分别为 1, 2, 3, 值分别为 a, b, c。编号为 0 的 group 表示这个 pattern 匹配的整个字符串,这个一般不包括在匹配组里,即 groups() 返回的是编号不为 0 的捕获组。这样设计的原因是,在 pattern 里加括号,表示我们对括号里的字符串比较感兴趣,而不是整体,如果只想匹配整体的话,中间就没必要加括号了。

以下是一段用于验证的 python 程序。

s = "abc"
pattern = r"(a)(b)(c)"
m = re.search(pattern, s)
print('groups:', m.groups())
print('group(0):', m.group(0))
print('group(1):', m.group(1))
print('group(2):', m.group(2))
print('group(3):', m.group(3))

输出:
groups: ('a', 'b', 'c')
group(0): abc
group(1): a
group(2): b
group(3): c

3. 非捕获组 (? )

非捕获组与捕获组的区别在于,非捕获组在左括号 "(" 右边加了一个问号 "?",并且括号内匹配的内容不会被当做一个捕获组,没有捕获组编号。

3.1 简单非捕获组

(?:pattern)
匹配 pattern 而不将其加入捕获组。

string: "abc"
pattern: "(?:a)(b)(c)"
这个表达式匹配得到的字符串是 "abc"(即 group(0) = abc)。
有两个捕获组,group(1) = a, group(2) = b。

3.2 零宽断言

先解释几个词:
Positive/Negative: 肯定/否定
Lookahead/Lookbehind: 前向/后向
Zero-Length: 零长度(零宽)
Assertion: 断言

再解释几个符号:
= 表示肯定
! 表示否定
< 表示负向(什么都没有表示正向)

把肯定、否定、正向、负向组合,就有了以下四种零宽断言的形式。

3.2.1 肯定正向零宽断言 (Positive Lookahead Zero-Length Assertion)。

pattern1(?=pattern2)

在匹配到 pattern1 后,向右(正向)匹配 pattern2,如果后面的字符串匹配 pattern2,则 pattern1 匹配成功,否则 pattern1 匹配失败。pattern2 是零(长度)宽断言,因此 pattern2 是不占长度的,即匹配后面的字符串的时候,是从匹配 pattern1 的字符串结尾的下一个字符开始匹配。

pattern: "(ab)c(?=-)"
对于字符串 "abc-abc", 匹配结果是第一个 "abc",有一个捕获组 "ab"。
对于字符串 "abc-abc-", 匹配结果是 ("abc", "abc"),有两个捕获组 ("ab", "ab")。

3.2.2 否定正向零宽断言 (Negative Lookahead Zero-Length Assertion)

pattern1(?!pattern2)

在匹配到 pattern1 后,向右(正向)匹配 pattern2,如果后面的字符串匹配 pattern2,则 pattern1 匹配失败, 否则 pattern1 匹配成功。pattern2 是零(长度)宽断言,因此 pattern2 是不占长度的,即匹配后面的字符串的时候,是从匹配 pattern1 的字符串结尾的下一个字符开始匹配。

string: "a-b"
pattern: "(\w)(?!-)"
匹配结果是 "b", 有一个捕获组 "b"。

3.2.3 肯定负向零宽断言 (Positive Lookbehind Zero-Length Assertion)

(?<=pattern1)pattern2

对于当前的字符,向左(负向)匹配 pattern1,如果 pattern1 匹配成功,则从当前字符开始匹配 pattern2,若 pattern2 匹配成功,则整个表达式匹配成功,否则整个表达式匹配失败;如果 pattern1 匹配失败,则不继续匹配 pattern2,而是认为整个表达式匹配失败,从后一个字符开始继续匹配。

string: "a-b"
pattern: "(?<=-)(\w)"
匹配结果是 "b", 有一个捕获组 "b"。

3.2.4 否定负向零宽断言 (Negative Lookbehind Zero-Length Assertion)

(?<!pattern1)pattern2

对于当前的字符,向左(负向)匹配 pattern1,如果 pattern1 匹配失败,则从当前字符开始匹配 pattern2,若 pattern2 匹配成功,则整个表达式匹配成功,否则整个表达式匹配失败;如果 pattern1 匹配成功,则不继续匹配 pattern2,而是认为整个表达式匹配失败,从后一个字符开始继续匹配。

string: "a-b"
pattern: "(?<!-)(\w)"
匹配结果是 "a", 有一个捕获组 "a"。

4. Reference

https://www.regular-expressions.info

5. License

本作品采用知识共享 署名-非商业性使用-相同方式共享 2.5 中国大陆 许可协议进行许可。要查看该许可协议,可访问 http://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 或者写信到 Creative Commons, PO Box 1866, Mountain View, CA 94042, USA。

原文链接 https://www.jianshu.com/p/37c1f7c58f26

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容