正则表达式用来实现字符串的检索、替换、匹配和验证。
- 实例引入
https://tool.oschina.net/regex/,该网站可用于常用正则表达式的匹配。例如:
匹配中文字符:[\u4e00-\u9fa5]
匹配URL的正则表达式:[a-zA-z]+://[^\s]*
匹配18位身份证号:^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$
其中a-z代表任意的小写字母,\s代表任意空白字符,*代表匹配前面的任意多个字符,一串正则表达式就是多个匹配规则的组合。
Python的re库提供整个正则表达式的实现。
- match
match方法会尝试从字符串的起始位置开始匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回None。例:
import re
content = 'Hello 123 4567 Hello_world'
print(len(content))
result = re.match('^Hello\s\d{3}\s\d{4}\s\w{11}', content)
print(result)
print(result.group())
print(result.span())
在match方法中,第一个参数是传入正则表达式,第二个参数是传入要匹配的字符串。group方法可以输出匹配到的内容,是正则表达式按照规则匹配的内容;span方法输出匹配到的范围,这是匹配到的结果字符串在原字符串中的位置范围。
匹配目标
可以使用小括号()将想要提取的子字符串括起来。()实际上标记了一个子表达式的开始和结束位置,被标记的每个子表达式依次对应每个分组,调用group方法传入分组的索引即可获取提取结果。例:
import re
content = 'Hello 1234567 Hello_world'
print(len(content))
result = re.match('^Hello\s(\d{7})\s\w{11}', content)
print(result)
print(result.group(1))
print(result.span())
可以看到其中数字部分的正则表达式被()括了起来,然后调用group(1)获取匹配结果。假如正则表达式后面还有用()包围的内容,那么可以依次用group(2)、group(3)等获取。
通用匹配
符号.可以匹配任意字符,*代表匹配前面的字符无限次,组合在一起.*就可以匹配任意字符。例:
import re
content = 'Hello 1231 23412 This is a regular expression demo'
result = re.match('^Hello.*demo$',content)
print(result)
print(result.group())
print(result.span())
贪婪与非贪婪
例:
import re
content = 'Hello 1234567 World'
result = re.match('^He.*(\d+).*ld$',content)
print(result)
print(result.group(1))
只得到7这个数字,因为涉及贪婪和非贪婪。在贪婪匹配下,.*会匹配尽可能多的字符。正则表达式中,.*后面是\d+,也就是至少一个数字,而且没有指定具体几个数字,因此,.*会匹配尽可能多的字符,这里就把123456都匹配了,只给\d+留下一个可满足条件的数字7,因此最后得到的内容就只有数字7。
而非贪婪匹配的写法是.*?,比通用匹配多了一个?。
修饰符
在正则表达式中,可以用一些可选标志修饰符来控制匹配的模式,例:
import re
content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*Demo$',content,re.S)#若不加参数re.S则报错
print(result.group(1))
re.S的作用是匹配内容包括换行符在内的所有字符。
修饰符 |
描述 |
re.I |
使匹配对大小写不敏感 |
re.L |
实现本地化识别匹配 |
re.M |
多行匹配,影响^和$ |
re.S |
使匹配内容包括换行符在内的所有字符 |
re.U |
根据Unicode字符集解析字符,影响\w、\W、\b和\B |
re.X |
能够给予你跟灵活的格式,以便将正则表达式书写得更易于理解 |
转义匹配
正则表达式定义了许多匹配模式,如.用于匹配除换行符以外的任意字符。但如果目标字符串里就包含.这个字符,在此字符前加反斜线\转义一下即可,例:
import re
content = '(百度)www.baidu.com'
result = re.match('\(百度\)www\.baidu\.com', content)
print(result)
结果成功匹配了原字符串。
- search
match方法从字符串的开头开始匹配,而search从在匹配时会扫描整个字符串,然后返回第一个匹配成功的结果。也就是说,正则表达式可以是字符串的一部分。在匹配时,search方法会依次以每个字符作为开头扫描字符串。直到找到第一个符合规则的字符串,然后返回匹配内容;如果扫描完还没有找到符合规则的字符串,就返回None。
由于绝大部分HTML文本包含换行符,所以需要尽量加上re.S修饰符,一面出现匹配不到的问题。
- findall
如果想要获取与正则表达式相匹配的所有字符串,就要借助findall方法了。
findall的返回结果是列表类型,需要通过遍历来依次获取每组内容。
- sub
除了使用正则表达式提取信息,有时候还需要借助它来修改文本。例如,想要把一串文本中的所有数字都去掉,如果只用字符串的replace方法未免太繁琐,可以借助sub方法。例:
import re
content = '662akdkad1khk21'
content = re.sub('\d+','',content)
print(content)
这里往sub方法的第一个参数中传入\d+以匹配所有的数字,往第二个参数中传入把数字替换成的字符串,第三个参数是原字符串。
在提取HTML文本中还可以用sub方法将节点去掉,只留下文本,然后再利用findall提取。
- compile
compile方法可以将正则字符串编译成正则表达式对象,以便在后面的匹配中复用。例:
import re
content1 = '2021-1-1 12:00'
content2 = '2021-12-1 01:00'
content3 = '2022-1-1 23:10'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern,'',content1)
result2 = re.sub(pattern,'',content2)
result3 = re.sub(pattern,'',content3)
print(result1,result2,result3)
这个实例有三个日期,我们想分别将这3个日期的时间去掉,可以借助sub方法。该方法的第一个参数是正则表达式,但是没有必要写三个同样的正则表达式,此时就可以借助compile方法将正则表达式编译成一个正则表达式对象,以便复用。