内置模块之re模块及正则表达式语法简述
内置模块之re模块
正则表达式应用场景
在很多网页中,要求输入手机号、邮箱等,会在我们还没输完的情况下就提示我们在“请输入正确的手机号”“邮箱格式不正确”等信息,这用到了文字匹配的功能:
其逻辑用python代码实现是这样的:
案例:京东注册手机号校验
基本需求:手机号必须是11位、手机号必须以13 15 17 18 19开头、必须是纯数字
'''纯python代码实现'''
while True:
# 1.获取用户输入的手机号
phone_num = input('请输入您的手机号>>>:').strip()
# 2.先判断是否是十一位
if len(phone_num) == 11:
# 3.再判断是否是纯数字
if phone_num.isdigit():
# 4.判断手机号的开头
if phone_num.startswith('13') or phone_num.startswith('15') or phone_num.startswith(
'17') or phone_num.startswith('18') or phone_num.startswith('19'):
print('手机号码输入正确')
else:
print('手机号开头不对')
else:
print('手机号必须是纯数字')
else:
print('手机号必须是11位')
而使用python+re正则表达式的代码实现简单很多:
import re
phone_number = input('please input your phone number: ')
if re.match('^(13|14|15|18)[0-9]{9}$', phone_number):
print('是合法的手机号码')
else:
print('不是合法的手机号码')
而使用正则表达式这一方法,我们注意到,python代码导入了re模块使用了一个功能,帮我们用一个表达式实现了判断是否符合十一位数、纯数字、手机号开头为某些组合
这一需求。
这就是正则表达式的应用场景。
正则表达式是一门独立的技术 ,所有编程语言都可以使用。
它的作用可以简单的概括为:利用一些特殊符号(也可以直接写需要查找的具体字符)的组合产生一些特殊的含义,然后去字符串中筛选出符合条件的字符串。
正则表达式怎么使用呢?我们大致可以通过以下几点来总结:
字符组
采取[]
括起来的一组字符,称为字符组,字符组中的所有字符为或关系,即当字符组中有一个字符匹配到了,就成功匹配:
正则表达式 | 解释 |
---|---|
[0123456789] | 匹配0到9任意一个数(全写) |
[0-9] | 匹配0到9任意一个数(缩写) |
[a-z] | 匹配26个小写英文字母 |
[A-Z] | 匹配26个大写英文字母 |
[0-9a-zA-Z] | 匹配数字或者小写字母或者大写字母 |
以[0-9]为例,我们尝试用几个待匹配字符串得到匹配结果
待匹配字符串 | 匹配结果 | 解释 |
---|---|---|
'6' | ['6'] | [0-9]中有6,所以可以匹配到6 |
'o' | ['None'] | 字母不符合条件 |
'78i2' | ['7','8','2'] | 每个匹配到的结果 ,都会从字符串中切出 |
'111' | ['1','1','1'] | 有几个1取几个1 |
特殊符号
我们同样可以通过特殊符号来匹配某一类字符。
其中加粗的为更加常用的一些特殊符号。
元字符 | 匹配内容 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\b | 匹配一个单词的结尾 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结尾 |
\W | 匹配非字母或数字或下划线 |
\D | 匹配非数字 |
\S | 匹配非空白符 |
a|b | 匹配字符a或字符b(或关系) |
() | 匹配括号内的表达式,也表示一个组 |
[...] | 匹配字符组中的字符 |
[^...] | 匹配除了字符组中字符的所有字符 |
例子:
正则 | 待匹配字符串 | 结果 | 解释 |
---|---|---|---|
\w | 'ab-ab-a' | ['a','b','a','b','a'] | 匹配字符串中的所有字母\数字\下划线 |
\w\w | 'ab-ab-a' | ['ab,'ab'] | 两个\w代表符合条件的两个字符连起来 |
[^a] | 'ab-ab-a' | ['b','-','b','-'] | 一个[。。。]仅表示匹配一个字符 |
例子2:
正则 | 待匹配字符 | 结果 | 说明 |
---|---|---|---|
海. | 海燕海娇海东 | 海燕海娇海东 | 匹配所有"海."的字符 |
^海. | 海燕海娇海东 | 海燕 | 只从开头匹配"海." |
海.$ | 海燕海娇海东 | 海东 | 只匹配结尾的"海.$" |
注意:区分^在[..]内外含义的不同,在外侧表示匹配字符串开头,在内侧表示取反。
量词
在上述的例子上我们发现,一个字符组或者特殊符号仅匹配字符串中的一位字符,如果想匹配多个字符就要将连接多个符号。
所以正则还为我们提供了量词,跟在某个字符组或特殊符号的右边表示匹配连续几个符合条件的字符。
量词 | 用法说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} |
重复n次 |
{n,} |
重复n次或更多次 |
{n,m} |
重复n到m次 |
对于上述表格,我们还需要进行进一步的举例说明:
如正则表达式为[a]
,待匹配字符串为'aaaa'
那么其匹配结果是['a','a','a','a']
而搭配量词,正则为[a]*
,同样的待匹配字符串,其匹配结果则为['aaaa',]
也就是说,*
所谓的重复零次或者多次,是指每次尽量多的拿,
即a可以拿不到或者拿无数次,但是一旦是连着的a,都会被[a]*
全收。
这也是正则表达式默认情况下所遵循的贪婪匹配法。
同样所有的量词都遵循在其最大限度的情况下切片走字符串中的满足条件的字符组合。
量词的例子
正则 | 待匹配字符 | 匹配结果 | 说明 |
---|---|---|---|
李.? | 李杰和李莲英和李二棍子 | 李杰 李莲 李二 | ?表示重复零次或一次,即只匹配"李"后面一个任意字符 |
李.* | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 | *表示重复零次或多次,即匹配"李"后面0或多个任意字符 |
李.+ | 李杰和李莲英和李二棍子 | 李杰和李莲英和李二棍子 | +表示重复一次或多次,即只匹配"李"后面1个或多个任意字符 |
李. | 李杰和李莲英和李二棍子 | 李杰和 李莲英 李二棍 | {1,2}匹配1到2次任意字符 |
[\d]+ | 456bdha3 | 456 3 | 表示匹配任意个数字,匹配到2个结果 |
贪婪匹配与非贪婪匹配
所有的量词都遵循在其最大限度的情况下切片走字符串中的满足条件的字符组合。
正则 | 待匹配字符 | 匹配结果 | 说明 |
---|---|---|---|
<.*> | <script>...</script> |
<script>...</script> |
默认为贪婪匹配模式,会匹配尽量长的字符串 |
<.*?> | r'\d' | <script> </script> |
加上?为将贪婪匹配模式转为非贪婪匹配模式,会匹配尽量短的字符串 |
我们本意是想匹配被<>
括住的字符内容,但是*
十分的贪婪,在遇到>
时,它并不会将>
的匹配权让给正则中的>
,而是继续不断的往后匹配无限个任意字符(贪婪的.*
)。
所以我们需要在量词*
后加上?
组成*?
来表示我还是有多少拿多少,但是当遇到紧贴*?
后的正则对应的字符时,就会将匹配权交给后面的正则,就如这里的<.*?>
中的>
就截停住了。
转义符
斜杠与字母的组合有时候有特殊含义,如:
\n
匹配的是换行符
\\n
匹配的是文本\n
用\来取消\n的特殊含义
\\\\n
匹配的是文本\\n
ps:python字符串中取消转义符的特殊含义,只需要在整个字符串的引号前加一个r
正则表达式使用建议
在日常的编程中,有很多诸如手机号格式匹配、邮箱格式匹配这些很常用的匹配,就不需要我们自己来编写正则表达式了,可以到网上找现成的。
因为这种匹配泛用性很广,所以一定有前人栽树,我们后人乘凉就行了。
re模块
python中的re模块可以让我们通过正则表达式来匹配字符串中符合条件的组合。
findall —— 匹配所有
import re
res = re.findall('e', 'leethon eat apple') # 第一个参数给正则,第二个参数是待匹配字符串
print(res) # ['e', 'e', 'e', 'e']
# 匹配到几个满足表达式'e'的,就返回几个,并组成列表
finditer —— 匹配所有返回迭代器
res = re.finditer('e', 'leethon')
print(res) # <callable_iterator object at 0x000001DA56E8A190>
print(list(res)) # [<re.Match object; span=(1, 2), match='e'>, <re.Match object; span=(2, 3), match='e'>]
拿到个迭代器,每个结果都为匹配对象
search —— 搜索到第一个
res = re.search('e', 'leethon')
print(res) # <re.Match object; span=(1, 2), match='e'>
# 匹配到第一个满足条件就停止
print(res.group()) # e
# 匹配对象可以通过.group的方式拿到匹配的对象
match —— 只匹配开头
res = re.match('e', 'leethon')
print(res) # None
# 只匹配开头,不满足则返回None,相当于把我们的正则表达式前加个^
compile —— 正则模板
obj = re.compile('\d{3}') # 当某一个正则表达式需要频繁使用的时候 我们可以做成模板
# 这个正则表达式就可以通过.的方式调用findall
res1 = obj.findall('23423422342342344')
res2 = obj.findall('asjdkasjdk32423')
print(res1, res2)
其他功能 —— split、sub、subn
ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
print(ret) # ['', '', 'cd']
ret = re.sub('\d', 'H', 'eva3jason4yuan4', 1) # 将数字替换成'H',参数1表示只替换1个
print(ret) # evaHjason4yuan4
ret = re.subn('\d', 'H', 'eva3jason4yuan4') # 将数字替换成'H',返回元组(替换的结果,替换了多少次)
print(ret) # ('evaHjasonHyuanH', 3)
re模块补充
分组优先
在特殊符号中,我们有提到( )
代表分组的意思。
res = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
print(res) # ['oldboy']
# findall分组优先展示: 优先展示括号内正则表达式匹配到的内容
res = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com')
print(res) # ['www.oldboy.com']
# 在组的括号中加入(?: ),就取消分组优先
分组别名
在findall中,优先展示分组,而在search和match拿到的match对象中,利用match对象.group()
方法默认拿到的是匹配的所有内容。
res = re.search('www.(?P<content>baidu|oldboy)(?P<hei>.com)', 'www.oldboy.com')
# 正则中含了两个组,并分别用?P<名字>的方式命名了每个分组
print(res.group()) # www.oldboy.com # 默认为所有内容
print(res.group('content')) # oldboy # 只显示content组中的内容
print(res.group(0)) # www.oldboy.com # group(0)相当于全部
print(res.group(1)) # oldboy # 按索引取组中内容
print(res.group(2)) # .com # 索引为2即第二组中的内容
print(res.group('hei')) # .com # 按命名取组中内容