正则表达式组与断言
正则表达式处理的对象是字符串,或者抽象地说,是一个对象序列,若要为字符串设定多个匹配规则并分别使用的话,那么使用正则表达式分组是更好的选择,这也体现了正则表达式的灵活与高效。
分组
分组就是用一对圆括号“()”括起来的正则表达式,匹配出的内容就表示一个分组。从正则表达式的左边开始看,看到的第一个左括号“(”表示第一个分组,第二个表示第二个分组,依次类推,需要注意的是,有一个隐含的全局分组(0分组),就是整个正则表达式。
分完组以后,要想获得某个分组的内容,直接使用group(num)和groups()函数去直接提取就行。
group(0) 是获取取得的字符串整体,group(1)是取出括号里面要匹配的分组内容,groups() 的功能就是分别取出所有的要匹配的值,不包含group(0)。
import re
a: str = "123abc456"
print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(0)) # 123abc456
print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(1)) # 123
print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(2)) # abc
print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(3)) # 456
print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups()) # ('123', 'abc', '456')
断言
断言是判断当前位置的前后是否匹配,但是不消耗任何字符,只用于匹配位置,而不是文本内容本身,这种结构就是断言。在匹配过程中,不占用字符,所以被称为**“零宽”**。
正则表达式的先行断言和后行断言一共有 4 种形式:
(?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
(?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
(?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
(?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)
这里面的 pattern 是一个正则表达式。
对于这 4 个断言的理解,可以从两个方面入手:
1、关于先行(lookahead)和后行(lookbehind):正则表达式引擎在执行字符串和表达式匹配时,会从前到后连续扫描字符串中的字符,设想有一个扫描指针指向字符边界处并随匹配过程移动。先行断言,是当扫描指针位于某处时,引擎会尝试匹配指针还未扫过的字符,先于指针到达该字符,故称为先行。后行断言,引擎会尝试匹配指针已扫过的字符,后于指针到达该字符,故称为后行。
2、关于正向(positive)和负向(negative):正向就表示匹配括号中的表达式,负向表示不匹配。
import re
b: str = "ABC1230321CBA"
print(re.search("2(?=3)", b)) # <re.Match object; span=(4, 5), match='2'>
print(re.search("2(?!3)", b)) # <re.Match object; span=(8, 9), match='2'>
print(re.search("(?<=A)B", b)) # <re.Match object; span=(1, 2), match='B'>
print(re.search("(?<!A)B", b)) # <re.Match object; span=(11, 12), match='B'>
- (?=pattern) 正向先行断言,代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配 pattern。
print(re.search("2(?=3)", b))
# <re.Match object; span=(4, 5), match='2'>
- (?!pattern) 负向先行断言,代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配 pattern。
print(re.search("2(?!3)", b))
# <re.Match object; span=(8, 9), match='2'>
- (?<=pattern) 正向后行断言,代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配 pattern。
print(re.search("(?<=A)B", b))
# <re.Match object; span=(1, 2), match='B'>
- (?<!pattern) 负向后行断言,代表字符串中的一个位置,紧接该位置之前的字符序列不能匹配 pattern。
print(re.search("(?<!A)B", b))
# <re.Match object; span=(11, 12), match='B'>