这个坑是个伤心事啊,确实日常工作用到的比较少,所以总结一下加深记忆吧;
Regular Expression,在代码中常简写为regex、regexp或RE;正则表达式是对字符串操作的一种逻辑公式,用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
- 创建和查找的基本步骤:
- 利用括号分组(group方法)
可以添加括号在正则表达式中创建‘分组’
如电话号码分区号和号码两个部分:
(\d\d\d)-(\d\d\d-\d\d\d\d)
其中第一对括号是第一组,第二对括号是第二组,向group()匹配对象中传入1或2时则分别返回匹配的两组信息,传入0或者不传入参数,将返回真哥哥匹配的文本;
>>> import re >>> phonenumregex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') >>> mo = phonenumregex.search('My number is 415-555-4242') >>> mo.group() '415-555-4242' >>> mo.group(1) '415' >>> mo.group(2) '555-4242'
>>> mo.groups() ('415', '555-4242')
一次获取所有分组,则可以使用groups()方法;
如果正则表达式需要匹配(),则应该使用转义字符\(和\) 表式括号;
- 构造更复杂的正则表达式
1.管道 | 类似于或,匹配许多表达式中的一个:
>>> heroRegex = re.compile(r'Batman|Tina Fey') >>> mo1 = heroRegex.search('Batman and Tina Fey') >>> mo1.group() 'Batman' >>> mo1 = heroRegex.search(' Tina Fey and Batman') >>> mo1.group() 'Tina Fey'
| 匹配返回第一次出现的匹配对象,如果需要匹配真正的|则需要使用转义字符 \| 来匹配 | ;
>>> batRegex=re.compile(r'Bat(man|mobile|copter|bat)') >>> mo = batRegex.search('Batmobile lost a wheel') >>> mo.group() 'Batmobile' >>> mo.group(1) 'mobile'
2.问号实现可选匹配;
?表示前面这个分组在这个模式中是可选的,如果有的话即进行匹配,?可以理解为匹配之前的分组零次或一次;
>>>betRegex = re.compile(r'(\d\d\d-)?\d\d\d-\d\d\d\d') >>> mo1 = betRegex.search('My number is 415-555-4242') >>> mo1.group() '415-555-4242' >>> mo2 = betRegex.search('My number is 555-4242') >>> mo2.group() '555-4242'
3.用*匹配零次或多次
*之前的分组可以在文本中出现任意次;
betRegex = re.compile(r'Bat(wo)*man') >>> mo1=betRegex.search('The Adventures of Batman') >>> mo1.group() 'Batman' >>> mo1=betRegex.search('The Adventures of Batwoman') >>> mo1.group() 'Batwoman' >>> mo1=betRegex.search('The Adventures of Batwowowowoman') >>> mo1.group() 'Batwowowowoman'
4.用+匹配一次或多次
+意味着匹配一次或多次,区别于*,+前面的分组必须至少出现一次;
>>> betRegex = re.compile(r'Bat(wo)+man') >>> mo1=betRegex.search('The Adventures of Batwoman') >>> mo1.group() 'Batwoman' >>> mo1=betRegex.search('The Adventures of Batwowowowoman') >>> mo1.group() 'Batwowowowoman' >>> mo1=betRegex.search('The Adventures of Batman') >>> mo1.group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group' >>> mo1 == None True
可以看到当wo一次都木有出现的时候,mo1返回的是None
5.用{}匹配出现的特定次数
在正则表示该分组之后加上{number}表示该分组匹配number次;
也可以在{}中指定一个范围,在{min,max}中可以限定出现的最大次数和最小次数;{min,}表示最少匹配min次,无上限;{,max}则表示最多匹配max次,无下限;
>>> haRegex = re.compile(r'(Ha){3}') >>> mo1 = haRegex.search('HaHaHa') >>> mo1.group() 'HaHaHa' >>> mo1 = haRegex.search('HaHa') >>> mo1.group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group' >>> mo1 == None True
可以看到因为{3}限定了匹配三次,所以HaHa匹配的结果是None。
6.贪心匹配和非贪心匹配
Python中的正则表达式默认是‘贪心’的,所以在有第二种选择的时候,会尽可能匹配最长的字符串;
{}的非贪心版本匹配尽可能最短的字符串,即在{}结束的后面加上一个问号;
>>> greedyRegex = re.compile(r'(Ha){3,5}') >>> mo1 = greedyRegex.search('HaHaHaHaHa') >>> mo1.group() 'HaHaHaHaHa' >>> nongreedyRegex = re.compile(r'(Ha){3,5}?') >>> mo1 = nongreedyRegex.search('HaHaHaHaHa') >>> mo1.group() 'HaHaHa'
>>> phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') >>> mo = phoneNumRegex.search('Cell:415-555-4242 Work 215-555-0000') >>> mo.group() '415-555-4242' >>> mo = phoneNumRegex.findall('Cell:415-555-4242 Work 215-555-0000') >>> mo.group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'list' object has no attribute 'group' >>> mo = phoneNumRegex.findall('Cell:415-555-4242 Work 215-555-0000') >>> mo ['415-555-4242', '215-555-0000']
>>> phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)') >>> mo = phoneNumRegex.findall('Cell:415-555-4242 Work 215-555-0000') >>> mo [('415', '555', '4242'), ('215', '555', '0000')]
匹配结果返回元组组成的列表,每个分组对应一个字符串,每完整匹配一次的结果是一个元组;
8.字符分类
\d | 0到9的任何数字 |
\D | 除0到9以外的任何字符 |
\w | 任何字母,数字或者下划线字符(可以认为是匹配‘单词’字符) |
\W | 除字母,数字和下划线以外的字符 |
\s | 空格,制表符或换行符(可以理解为匹配‘空白’字符) |
\S | 除空格,制表符和换行符之外的任何字符 |
>>> xmasregex = re.compile(r'\d+\s\w+') >>> mo = xmasregex.findall('12 drummers,11 pipers,10 loards') >>> mo ['12 drummers', '11 pipers', '10 loards']
善变的正则表达式中\d+表示匹配阿拉伯数字至少一次\s表示匹配空格字符\w+ 表示匹配字幕,数字或者下划线字符至少一次,所以\d+\s\w+要匹配的文本是有一个或者多个数字,然后跟着一个空白字符,接下来是一个或者多个字母/数字/下划线字符;
9.用[]建立自己的字符分类
因为字符分类的范围很广,所以如果只想匹配比如某几个阿拉伯数字或者某几个某几个字母符号,可以用[]建立所要的匹配的文本;
[a-zA-Z0-9]将匹配所有的大小写字符和阿拉伯数字;
[^aeio]将匹配到非后面的字符类;
[]内的匹配,普通的正则表达式符号不会被解释,所以[]内需要匹配./*?()等字符时,可以直接写入,不需要加上\进行注释;
10.^和$
>>> Regex1 = re.compile(r'^\d') >>> Regex1 = re.compile(r'^\d+$') >>> mo=Regex1.search('1234567890').group() >>> mo '1234567890' >>> mo=Regex1.search('123456xy7890').group() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'NoneType' object has no attribute 'group' >>> Regex1.search('123456xy7890') == None True
11.通配字符
.字符成为‘通配符’,可以匹配除了换行符之外的所有字符;
不过.只匹配一个字符,真正的句点则需要要用斜杠\转义;
>>> arRegex = re.compile(r'.at') >>> arRegex.findall('The Cat in the hat sat on the flat mat.') ['Cat', 'hat', 'sat', 'lat', 'mat']
12. .*匹配所有字符
.*可以匹配任意字符,使用贪心模式进行匹配尽可能多的文本;
.*?则是非贪心模式匹配尽可能短的文本;
>>> Regex1 = re.compile(r'<.*>') >>> mo = Regex1.search('<To Serve man> for dinner.>') >>> mo <_sre.SRE_Match object; span=(0, 27), match='<To Serve man> for dinner.>'> >>> mo.group() '<To Serve man> for dinner.>' >>> Regex2 = re.compile(r'<.*?>') >>> mo = Regex2.search('<To Serve man> for dinner.>') >>> mo.group() '<To Serve man>'
13.传入第二个参数进行匹配
前面总结.句点可以匹配除换行以外的所有一个字符,通过传入re.DOTALL作为re.compile()的第二个参数,则此时可以让句点匹配所有字符,包括换行符;
如果只关心匹配字母,并不在乎哒谢谢,可通过传入re.IGNORECASE或者re.I作为re.compile的第二个参数,则针对[]或者直接列出的字母将忽略大小写进行匹配;
如果要匹配复杂的文本模式, 可能需要很长的正则表达式,此时可以传入re.VERBOSE作为re.compile的第二个参数,忽略正则表达式中的空白符,换行符和注释;
r加上三重引号可以输入多行字符串,和Python读取字符串一样;