分组
import re
a = '123.123.123.aaa.aaa.aaa'
reg = '(\d{3})(.\\1){2}.(\w{3})(.\\3){2}'
res = re.search(reg,a)
print(res) # <re.Match object; span=(0, 23), match='123.123.123.aaa.aaa.aaa'>
引用分组
上例中\\1
代表引用第一个分组\d{3}
;\\3
代表引用第三个分组\w{3}
import re
a = '123.234.345.456'
b = '123.123.123.123'
reg = '(\d{1,3})(.\\1){3}'
resa = re.search(reg,a)
resb = re.search(reg,b)
print(resa,'\n',resb)
'''输出
None
<re.Match object; span=(0, 15), match='123.123.123.123'>
'''
引用文本内容
由上例,后向引用,引用的仅仅是文本内容,而不是正则表达式
也就是说,组中的内容一旦匹配成功,后向引用,引用的就是匹配成功后的内容,引用的是结果,而不是表达式。
分组编号规律
无论括号如何嵌套,分组的编号都是根据开括号出现的顺序来计数的;开括号是从左至右数起第多少个开括号,分组编号就是多少。如下图:
断言
指明某个字符串前边或者后边,将会出现满足某种规律的字符串
如:(?<=<title>).*(?=</title>) 或者 (?<=<(title)>).*(?=</\\1>)
a = '<title>这里是标题内容</title>'
reg = '(?<=<title>).*(?=</title>)'
# 或
# reg = '(?<=<(title)>).*(?=</\\1>)'
resa = re.search(reg,a)
print(resa) # <re.Match object; span=(7, 14), match='这里是标题内容'>
- (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
它断言自身出现位置的后边能匹配表达式exp - (?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
它断言自身出现位置的后边不能匹配表达式exp - (?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
它断言自身出现位置的左边能匹配表达式exp - (?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)
它断言自身出现位置的左边不能匹配表达式exp
例:匹配 中国人的“人”而不匹配 外国人的“人”
b = '外国人中国人'
reg1 = '(?<!外国)人'
reg2 = '人'
res1 = re.search(reg1,b)
res2 = re.search(reg2,b)
print(res1,'\n',res2)
'''输出
<re.Match object; span=(5, 6), match='人'>
<re.Match object; span=(2, 3), match='人'>
'''
例:匹配 中国制造的“中国”
b = '中国生产中国制造'
reg1 = '中国(?!生产)'
res1 = re.search(reg1,b)
print(res1)
'''输出
<re.Match object; span=(4, 6), match='中国'>
'''
如同^代表开头,$代表结尾,\b代表单词边界一样,先行断言和后行断言也有类似的作用,它们只匹配某些位置,在匹配过程中,不占用字符,所以被称为“零宽”。所谓位置,是指字符串中(每行)第一个字符的左边、最后一个字符的右边以及相邻字符的中间(假设文字方向是头左尾右)
理解
关于先行(lookahead)和后行(lookbehind)
正则表达式引擎在执行字符串和表达式匹配时,会从头到尾(从前到后)连续扫描字符串中的字符,设想有一个扫描指针指向字符边界处并随匹配过程移动。先行断言,是当扫描指针位于某处时,引擎会尝试匹配指针还未扫过的字符,先于指针到达该字符,故称为先行。后行断言,引擎会尝试匹配指针已扫过的字符,后于指针到达该字符,故称为后行。
关于正向(positive)和负向(negative)
正向就表示匹配括号中的表达式,负向表示不匹配。