英文中不少单词中都有重叠出现的字母,比如shoot
或beep
,如果希望检查某个单词是否包含重叠出现的字母,该怎么解决?
匹配字母表达式是[a-z]
(这里暂不考虑大写的情况)。但“重叠出现”的字母,取决于第一个[a-z]
运行时的匹配结果,而不能预先设定。也就是说必须“知道”之前匹配的确切内容:如果前面匹配的是e,则后面只能是e;如果前面匹配的是a,后面就只能匹配a。
上一节讲到了引用分组,能引用某个分组内的子表达式匹配的文本,但是引用都是在匹配完成后进行的,能不能在正则表达式中引用呢?
答案是可以的,这种功能叫做反向引用(back-reference),它允许在正则表达式内部引用之前(左侧)的捕获分组匹配的文本,其形式也是\num
,其中num
表示所引用分组的编号,编号规则与之前介绍的相同。
根据反向引用,查找连续重叠字母的表达式就是([a-z])\1
,其中的[a-z]
匹配第一个字母,再用括号将匹配分组,然后用\1
来反向引用。
例3-24 用反向引用匹配重复字母
print(re.search(r'([a-z])\1', 'aabb')) # 'aa'
print(re.search(r'([a-z])\1', 'cc')) # 'cc'
print(re.search(r'([a-z])\1', 'mn')) # None
在日常开发中,我们经常需要反向引用来建立前后联系。最常见的就是解析html tag。在html语法中,tag
包括open tag
和close tag
,它们经常是成对出现的,比如<bold>马小跳</bold>
、<h1>title</h1>
。
有了反向引用,就可以先匹配open tag
,再匹配其他内容,直到最近的close tag
为止:用([^>]+)
匹配open tag
的tag name
,在匹配close tag
时,用\1
引用之前匹配的tag name
。
例3-25 用反向引用匹配成对的tag
pairedTagRegex = re.compile(r'<([a-zA-Z0-9]+)(\s*[^>]+)?>[\s\S]*?</\1>')
print(pairedTagRegex.search('<bold name="">马小跳</bold>')) # <bold name="">马小跳</bold>
print(pairedTagRegex.search('<h1 src="mxt">马小跳</h1>')) # <h1 src="mxt">马小跳</h1>
print(pairedTagRegex.search('<h1><span>马小跳</span></h1>')) # <span>马小跳</span>
反向引用还可以用在其他很多地方,比如处理中文文本时,查找“浩浩荡荡”、“明明白白”之类aabb
或者“如火如荼”、“越来越好”这类abab
的四字词语。
关于反向引用,还有一点需要强调:反向引用重复的是对应捕获分组匹配的文本,而不是之前的表达式;也就是说,反向引用的是由之前表达式决定的具体文本,而不是符合某种规则的未知文本。