正则表达式括号
在正则表达式中我们可以使用括号将一部分正则表达式打包,形成一个新的子表达式,以及匹配其中的内容。这些被括起来的表达式可以使用括号进行分组,提高正则表达式的灵活性以及提高正则表达式的可读性。
分组表达式
在正则表达式中,我们可以通过括号将一段表达式分成一组,并进行匹配。在匹配成功后,我们可以通过对应编号来获取对应组的值。比如我们有一个字符串:abcd12345efgh67890
,现在我们想匹配其中前面四个字符以及后面五个字符,可以这样实现:
import re
# 匹配前面的四个字符以及后面的五个字符
m = re.search(r"(?P<first>\w{4}).{1,10}(?P<last>\w{5})", "abcd12345efgh67890")
print(m.groupdict()) # {'first': 'abcd', 'last': '67890'}
我们可以使用(?P<name>pattern)
的方式,来指定一个分组,其中name
是分组的名字,pattern
是需要匹配的正则表达式内容。在匹配结束后,我们可以通过groupdict()
方法获取这些分组的内容,以字典的形式返回。
非捕获分组
有时候,我们不需要获取匹配到的分组内容,仅仅是需要分组的正则表达式来提高每个子表达式的可读性。在这种情况下,我们可以使用非捕获分组。非捕获分组的语法是(?:pattern)
,其中pattern
为子表达式的正则表达式。
import re
# 使用非捕获分组
m = re.search(r"\w{4}(?:\d{5})(\w{5})", "abcd12345efgh67890")
print(m.groups()) # ('efgh',)
# 不使用非捕获分组
m = re.search(r"\w{4}\d{5}(\w{5})", "abcd12345efgh67890")
print(m.groups()) # ('efgh',)
嵌套分组
有时候,我们需要将多个子表达式嵌套形成一个新的表达式。在这种情况下,我们可以使用嵌套分组。
import re
# 嵌套分组
m = re.search(r"(\w{4}(?:\d{5})(\w{5}))+", "abcd12345efgh6789056789ijklm12345opq")
print(m.groups()) # ('ijklm67890', 'ijklm', '67890')
在上面的例子中,我们嵌套了一个非捕获分组之后,又用一个捕获分组将其包含。这样做的结果是我们得到了三个分组:
ijklm67890
ijklm
67890
之所以会得到三个分组,是因为我们的组是由嵌套的非捕获分组构成的,所以在进行匹配时它会将最外层捕获分组的结果进行记录,同时也会将每一个内部的非捕获分组匹配的结果进行记录。
后向引用
当我们需要在正则表达式中匹配一些字符串时,如果这些字符串出现的位置有依赖性,那么我们可以使用后向引用。后向引用可以对之前匹配到的分组进行引用。比如我们有一个日期的字符串,需要判断这个日期是否正确,正确日期的格式是YYYY-MM-DD
,我们可以这样实现:
import re
# 后向引用
date_regex = r"(\d{4})-(\d{2})-(\d{2})"
m = re.search(date_regex, "2022-03-20")
if m:
year, month, day = m.group(1), m.group(2), m.group(3)
if int(year) < 2000:
print("Error: year should be greater than 2000")
elif int(month) > 12 or int(day) > 31:
print("Error: invalid date format")
else:
print("Valid date!")
else:
print("Error: date format should be YYYY-MM-DD")
在上面的例子中,我们定义了一个日期的正则表达式(\d{4})-(\d{2})-(\d{2})
,分别捕获了年、月和日三个分组。在匹配时,我们可以使用group()
方法获取这些分组的内容。接着我们进行了一些校验,包括年份必须大于2000,月份不得大于12,日期不得大于31。
结论
正则表达式括号在正则表达式中发挥非常重要的作用,不仅可以提高正则表达式的灵活性和可读性,还能够在需要的时候引用之前成功匹配到的分组,提高正则表达式的匹配效率。比较常用的分组方式有捕获分组、非捕获分组和嵌套分组,在实践中需要合理运用各种分组技巧,才能更好地应对各种匹配场景。