使用re模块可以采用预编译然后使用编译过的方法,也可以不编译就直接使用函数,但无论是方法还是函数,名字都是一样的
match(), search(), sub(), subn(),findall(),finditer,split(),compile()
表达式符号 | 作用 |
---|---|
re1|re2 | 匹配表达式 re1 或表达式 re2 |
. | 匹配任意一字符(除了\n) |
^ | 匹配字符串起始部分 |
$ | 匹配字符串终止部分 |
* | 匹配 0 或多次前面出现的字符 |
+ | 匹配 1 次或多次前面出现的字符 |
? | 匹配 0 或 1 次前面出现的字符 |
{N} | 匹配 N 次前面出现的正则表达式 |
[…] | 匹配字符集的任一字符 |
[^…] | 不匹配字符集里的任意字符 |
[x-y] | 匹配 x~y 中的任意字符 |
(…) | 匹配里头封闭的正则表达式,并存为一个分组 |
特殊字符 | 作用 |
---|---|
\d | 匹配十进制数字字符,与 [0-9] 一致,相反为 \D |
\w | 匹配任意字母或数字字符,与[A-Za-z0-9] 一到处,相反为 \W |
\s | 匹配任何空格字符,与 [\n\t\r\v\f] 相同,相反 \S |
\b | 匹配任何单词边界, 相反 \B |
\N | 匹配已存的子组 |
\A(\Z) | 匹配字符串的起始(结束) |
import re
你需要处理一个电话薄,需要里面的电话号码是这样的:
xxxx-xxxxxxx,如:0777-8888888
对应的正则表达式:r'\d{4}-\d{7}'
免责声明:下面提到的号码相关的正则表达式仅供参考
作用:从字符串的起始部分进行匹配
函数使用:re.match(<正则表达式>, <用来匹配的字符串>)
完全匹配
>>> re.match(r'0777-8888888','0777-8888888')
#匹配成功,返回一个匹配对象
'0777-8888888'成功匹配,返回一个匹配对象。如果匹配不成功的话则返回None
不完全匹配
>>> re.match(r'\d{4}-\d{7}','abc0777-8888888') #匹配失败,返回None
>>> re.match(r'\d{4}-\d{7}','0777-8888888abc')
因为是从字符串起始部分匹配,所以 'abc0777-8888888' 匹配失败,没有返回值,如果不想从起始位置匹配,可以使用下面将要介绍的 search()
作用:搜索任何位置匹配的字符串
函数使用:re.search(<正则表达式>, <用来匹配的字符串>)
>>> re.search(r'\d{4}-\d{7}','abc0777-8888888def')
跟 match() 不一样的地方是 search() 是查找匹配的模式,任意位置的字符串都能被匹配到
假如现在不仅要匹配电话号码,还要匹配手机号码(如:+86-133123456789),这时就需要匹配多个字符串,可以使用择一匹配符号“|”。
正则表达式:r'\d{4}-\d{7}|+86-\d{11}'
>>> r = r'\d{4}-\d{7}|\+86-\d{11}'
>>> re.search(r,'18888888888')
>>> re.search(r,'+86-18888888888')
>>> re.match(r,'0777-8888888')
当匹配到字符串时我们会得到一个匹配对象,可以用group()方法,返回匹配的字符串
>>> r = r'\d{4}-\d{7}|\+86-\d{11}'>>> m = re.search(r,'+86-18888888888')
>>> if m is not None:
... m.group()
...
'+86-18888888888'
使用 if 去判断是否为空是因为当字符串不匹配时会返回 None 而不是一个匹配对象,这时调用方法group() 会报错 AttributeError
还有另外一个方法 groups() 后面会讲到
手机号码并不是全部的11位数就能匹配的,只有像 188 开头的才可能是手机号码,使用字符集来筛选
正则表达式:r'1[3,5,7,8,9]\d{9}'
表示以1开头,第二位为3,5,7,8或9的11位数字组合,[…] 表示匹配里面中的任一一个字符
>>> r = r'1[3,5,7,8,9]\d{9}'
>>> m = re.match(r,'18812345678')
>>> m.group()
'18812345678'
>>> re.match(r,'123123456789')
显然上面的手机号码正则表达式还远不及需求,133开头可能是手机号码,但如果134开头的不是呢,这时可以使用分组
>>> r = r'1(33|35|37|57|77|88|97)\d{8}'
>>> re.match(r,'13512345678')
>>> re.match(r,'13412345678') # 匹配不到
如果我们把之前的电话号码分组,如:r'(\d{4})-(\d{7})'
>>> r = r'(\d{4})-(\d{7})'
>>> m = re.match(r, '0777-1234567')
>>> m.group()
'0777-1234567'
>>> m.group(0)
'0777-1234567'
>>> m.group(1)
'0777'
>>> m.group(2)
'1234567'
>>> m.groups()
('0777', '1234567')
group() 用于以普通的方式显示所有的匹配部分,加上编号可用于获取各个匹配的子组
groups()方法用来获取一个包含所有子组的字符串元组
>>> r = r'(\d{4})-(\d{7})'
>>> m = re.match(r, '0777-1234567')
>>> m.group() # 完整匹配
'0777-1234567'
>>> m.group(0) #完整匹配
'0777-1234567'
>>> m.group(1) #子组1
'0777'
>>> m.group(2) #子组2
'1234567'
>>> m.groups() #全部子组
('0777', '1234567')
>>> r = r'\d{4}-\d{7}'
>>> m = re.match(r, '0777-1234567abcd')
>>> m.group()
'0777-1234567'
>>> m.groups() #没有子组
()
r'^\d{4}-\d{7}'
表示起始匹配正则表达式
r'\d{4}-\d{7}$'
表示结尾匹配正则表达式
r'^\d{4}-\d{7}$'
表示起始和结尾结尾匹配正则表达式
>>> r = r'\d{4}-\d{7}$' #设置结束边界
>>> m = re.match(r, '0777-1234567')
>>> if m is not None:
... m.group()
...
'0777-1234567'
>>> m = re.search(r, '0777-1234567')
>>> m.group()
'0777-1234567'
>>> m = re.search(r, '0777-1234567abcd')
>>> if m is not None:
... m.group()
… #没匹配到
>>> r = r'^\d{4}-\d{7}' #设置起始边界
>>> re.search(r, 'abcd0777-12345677')
>>> #没匹配到
作用:相询全部不重复出现的字符串
使用:
re.findall(<正则表达式>, <作用的字符串>)
re.finditer(<正则表达式>, <作用的字符串>)
>>> r = r'\d{4}-\d{7}'
>>> re.findall(r, 'Tom:0111-1111111,Bob:0222-2222222')
['0111-1111111', '0222-2222222']
>>> re.findall(r, 'Tom:0000,Bob:0000')
[]
与 match() 和 search() 不同的是 findall() 相询全部不重复出现的字符串,并一直返回一个列表,匹配不到也是一个空的字符表。不重复是什么意思呢?如:
>>> r = r'\d{4}-\d{7}'
>>> re.findall(r,'0770-1234567-1234567')
['0770-1234567']
上面的列子字符串可匹配的情况有0770-1234567和4567-1234567,但由于4567在第一次查询已经使用,不会被重复使用,所以下次查词是从-1234567开始查找
使用分组,让findall() 只返回子组匹配的内容,如想只提取电话号码中的号码而不含区号,可以这样做
>>> r = r'\d{4}-(\d{7})'
>>> re.findall(r, 'Tom:0111-1111111,Bob:0222-2222222')
['1111111', '2222222']
finditer(),和findall()类似,但finditer返回的是一个迭代器,能节省内存的使用
>>> r = r'(\d{4})-(\d{7})'
>>> m = re.finditer(r,'Tom:0111-1111111,Bob:0222-2222222')
>>> [x.groups() for x in m]
[('0111', '1111111'), ('0222', '2222222')]
>>> m = re.finditer(r,'Tom:0111-1111111,Bob:0222-2222222')
>>> m.__next__().groups()
('0111', '1111111')
>>> m.__next__().groups()
('0222', '2222222')
作用:搜索匹配的字符串部分并以某种形式做替换
使用:
re.sub(<正则表达式>, <用来替换的字符串>, <作用字符串>)
re.subn(<正则表达式>, <用来替换的字符串>, <作用字符串>)
如:‘Miss.Tom:0111-1111111,Miss.Bob:0222-2222222’,想替换用户信息中的称呼“Miss”为 “Mr”
>>> re.sub('Miss', 'Mr','Miss.Tom:0111-1111111,Miss.Bob:0222-2222222')
'Mr.Tom:0111-1111111,Mr.Bob:0222-2222222'
>>> re.subn('Miss', 'Mr','Miss.Tom:0111-1111111,Miss.Bob:0222-2222222')
('Mr.Tom:0111-1111111,Mr.Bob:0222-2222222', 2)
可以看出subn还回返回一个替换的总数
还有一个神奇的用法。分组调换,使用 \N 来调用分组,N 表示分组编号
如:下面出现称呼和名称位置错位,调换过来
>>> info = 'TomMr.:0111-1111111,BobMr.:0222-2222222'
>>> re.sub(r'(\w+)(Mr\.)(\:\d{4}-\d{7})', r'\2\1\3', info)
'Mr.Tom:0111-1111111,Mr.Bob:0222-2222222'
作用:分割字符串,返回一个列表
使用:re.split(<分割位置字符串>, <被分割字符串>)
如:基于“:”字符分割
>>> re.split(':','Mr.Tom:0111-1111111,Mr.Bob:0222-2222222')
['Mr.Tom', '0111-1111111,Mr.Bob', '0222-2222222']
>>> re.split(':','Mr.Tom:0111-1111111,Mr.Bob:0222-2222222',1) #指定最大分割次数
['Mr.Tom', '0111-1111111,Mr.Bob:0222-2222222']
使用 compile() 可以将正则表达式预编译为一个对象,接着就可以重复调用该正则表达式
>>> c = re.compile(r'\d{4}-(\d{7})')
>>> c.search('Mr.Tom:0111-1111111,Mr.Bob:0222-2222222')
>>> c.findall('Mr.Tom:0111-1111111,Mr.Bob:0222-2222222')
['1111111', '2222222']
举个粟子
字符串:'
想提取 div 标签里的内容 123 和 abcd
正则表达式(贪婪匹配):r'
>>> re.findall(r'(\w.*)', '123abcd')
['123
正则表达式(非贪婪匹配):r'
>>> re.findall(r'(\w.*?)', '123abcd')
['123', 'abcd']
“?”在这里表示以尽量少的次数匹配
参考自:《Python 核心编程(第三版)》