括号不仅仅能把有联系的元素归拢起来并分组,还有其他的作用——使用括号后,正则表达式会保存每个分组真正匹配的文本,等到匹配完成后,通过group(num)
之类的方法“引用”分组在匹配时捕获的内容。其中num
表示对应括号的编号,括号分组的编号是从左向右计数,从1开始。因为“捕获”了文本,所以这种功能叫做捕获分组(capturing group)。对应的,这种括号叫做捕获型括号。
例3-16 引用捕获分组
print(re.search(r"(\d{4})-(\d{2})-(\d{2})", "2018-12-13").group(1)) # 2018
print(re.search(r"(\d{4})-(\d{2})-(\d{2})", "2018-12-13").group(2)) # 12
print(re.search(r"(\d{4})-(\d{2})-(\d{2})", "2018-12-13").group(3)) # 13
分组的编号从1开始。不过也有编号为0的分组,它是默认存在的,对应整个表达式匹配的文本。在很多语言中,调用group方法,如果不给出参数num,默认就等于调用group(0)
。
例3-17 编号为0的分组
print(re.search(r"(\d{4})-(\d{2})-(\d{2})", "2018-12-13").group()) # 2018-12-13
print(re.search(r"(\d{4})-(\d{2})-(\d{2})", "2018-12-13").group(0)) # 2018-12-13
有些表达式可能包括嵌套嵌套的括号,但是这都不影响编号:无论括号如何嵌套,分组的编号都是根据开括号的出现顺序来技术的;开括号是从左向右数起的第几个,这个括号分组的编号就是多少。
例3-18 嵌套括号的分组编号
regex = re.compile(r'(((\d{4})-(\d{2}))-(\d{2}))')
print(regex.search("2018-12-13").group(0)) # 2018-12-13
print(regex.search("2018-12-13").group(1)) # 2018-12-13
print(regex.search("2018-12-13").group(2)) # 2018-12
print(regex.search("2018-12-13").group(3)) # 2018
print(regex.search("2018-12-13").group(4)) # 12
print(regex.search("2018-12-13").group(5)) # 13
例3-19 利用分组提取超链接的详细信息(注意处理空白和单双引号)
regex = re.compile(r'<a\s+href\s*=\s*[\'"]?([^\'"\s]+)[\'"]?>([^<]+)</a>')
print(regex.findall('<a href="www.baidu.com">百度一下</a>')) # [('www.baidu.com', '百度一下')]
print(regex.findall("<a href = 'www.360.com'>360</a>")) # [('www.360.com', '360')]
print(regex.findall("<a href =https://souhu.com>搜狐</a>")) # [('https://souhu.com', '搜狐')]
引用分组捕获的文本,不仅用于数据提取,也可以用于替换。比如希望将yyyy-mm-dd
格式的日期变为mm/dd/yyyy
,就可以使用正则表达式进行替换。
在python中进行正则表达式替换的方法是re.sub(pattern, replacement, string)
,其中pattern
是用来匹配被替换文本的表达式,replacement
是要替换成的文本,string
是要进行替换操作的字符串(原始字符串)。
例3-21 正则表达式替换
print(re.sub(r'[a-z]', 'a', '1m1n1x')) # 1a1a1a
在replacement
中也可以引用分组,形式是\num
,其中的num是对应分组的编号。不过,replacement
并不是一个正则表达式,而是一个普通字符串。根据字符串中的转义规定,\t
是制表符,\n
是换行符,\1
、\2
却不是字符串中的合法转义序列,所以也必须指定replacement
为原生字符串。
例3-22 在替换中使用分组
regex = re.compile(r"(\d{4})-(\d{2})-(\d{2})")
print(regex.sub(r"\2/\3/\1", "2018-12-13")) # 12/13/2018
print(regex.sub(r"\1年\2月\3日", "2018-12-13")) # 2018年12月13日
值得注意的是,如果想在replacement
中引用整个表达式匹配的文本,不能使用\0
,即便使用原生字符串也不行。因为在字符串中,\0
开头的转义序列通常表示用八进制形式表示的字符,\0
本身表示ascii字符编码为0的字符。如果一定要引用整个表达式匹配的文本,可以稍加变通,给整个表达式加上一对括号,之后用\1
来引用。
例3-23 在替换中,使用\1
代替\0
# ASCII编码为0的字符无法显示
print(re.sub(r'(\d{4})-(\d{2})-(\d{2})', '\0', '2018-12-13')) # ''
print(re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\0', '2018-12-13')) # ''
# 变通一下
print(re.sub(r'((\d{4})-(\d{2})-(\d{2}))', r'[ \1 ]', '2018-12-13')) # [ 2018-12-13 ]
网友评论