python3--re模块:正则表达式
怎么判断一个手机号码是否符合规范?
根据手机号码一共11位并且只以13,14,15,18开头的数字这些特点,写了一段代码如下:
while True:
phone_number = input('please input your phone number : ')
if len(phone_number) == 11 and phone_number.isdigit() and (phone_number.startswith('13')
or phone_number.startswith('14') or phone_number.startswith('15')
or phone_number.startswith('18')):
print('是合法的手机号码')
else:
print('不是合法的手机号码')
执行结果
上面代码效果是可以实现,但是代码可读性差,正则怎么写呢?
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('不是合法的手机号码')
执行结果
please input your phone number : 13971604811
是合法的手机号码
身份证验证是否合法?
import re
while True:
ss = input('请输入你的身份证号: ').strip()
if re.match('^[1-9]d{14}(d{2}[dx])$', ss):
print('身份证合法!')
break
else:
print('不合法')
执行结果
从文件file中找出所有的手机号码--正则,文件中自行添加一些内容,加手机号码.
import re
with open('file', 'r')as f:
l = []
for i in f:
ret = re.findall('1[3-9]d{9}', i)
l.extend(ret)
print(l)
执行结果
['13864814521', '13475695414']
正则表达式
字符组 : [字符组]
在同一个位置可能出现的各种字符组成了一个字符组,在正则表达式中用[]表示
字符分为很多类,比如数字、字母、标点等等。
假如你现在要求一个位置"只能出现一个数字",那么这个位置上的字符只能是0、1、2...9这10个数之一
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
[0123456789] |
8 |
True |
在一个字符组里枚举合法的所有字符,字符组里的任意一个字符和"待匹配字符"相同都视为可以匹配 |
[0123456789] |
a |
False |
由于字符组中没有"a"字符,所以不能匹配 |
[0-9] |
7 |
True |
也可以用-表示范围,[0-9]就和[0123456789]是一个意思 |
[a-z] |
s |
True |
同样的如果要匹配所有的小写字母,直接用[a-z]就可以表示 |
[A-Z] |
B |
True |
[A-Z]就表示所有的大写字母 |
[0-9a-fA-F] |
e |
True |
可以匹配数字,大小写形式的a~f,用来验证十六进制字符 |
字符组:字符组代表一个字符位置上可以出现的所有
范围: 根据asc码来的,范围必须是从小到大的指向,一个字符组中可以有对个范围
字符:
元字符 |
匹配内容 |
---|---|
. |
匹配除换行符以外的任意字符 |
w |
匹配字母或数字或下划线 |
s |
匹配任意的空白符 |
d |
匹配数字 |
n |
匹配一个换行符 |
t |
匹配一个制表符 |
b |
匹配一个单词的结尾 |
^ |
匹配字符串的开始 |
$ |
匹配字符串的结尾 |
W |
匹配非字母或数字或下划线 |
D |
匹配非数字 |
S |
匹配非空白符 |
a|b |
匹配字符a或字符b |
() |
匹配括号内的表达式,也表示一个组 |
[...] |
匹配字符组中的字符 |
[^...] |
匹配除了字符组中字符的所有字符 |
量词:
量词 |
用法说明 |
---|---|
* |
重复零次或更多次 |
+ |
重复一次或更多次 |
? |
重复零次或一次 |
{n} |
重复n次 |
{n,} |
重复n次或更多次 |
{n,m} |
重复n到m次 |
. ^ $
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
海. |
海燕海娇海东 |
海燕海娇海东 |
匹配所有"海."的字符 |
^海. |
海燕海娇海东 |
海燕 |
只从开头匹配"海." |
海.$ |
海燕海娇海东 |
海东 |
只匹配结尾的"海.$" |
* + ? { }
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
李.? |
李杰和李莲英和李二棍子 |
李杰李莲李二 |
?表示重复零次或一次,即只匹配"李"后面一个任意字符 |
李.* |
李杰和李莲英和李二棍子 |
李杰和李莲英和李二棍子 |
*表示重复零次或多次,即匹配"李"后面0或多个任意字符 |
李.+ |
李杰和李莲英和李二棍子 |
李杰和李莲英和李二棍子 |
+表示重复一次或多次,即只匹配"李"后面1个或多个任意字符 |
李.{1,2} |
李杰和李莲英和李二棍子 |
李杰和李莲英李二棍 |
{1,2}匹配1到2次任意字符 |
注意:前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
李.*? |
李杰和李莲英和李二棍子 |
李李李 |
惰性匹配 |
字符集[][^]
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
李[杰莲英二棍子]* |
李杰和李莲英和李二棍子 |
李杰李莲英李二棍子 |
表示匹配"李"字后面[杰莲英二棍子]的字符任意次 |
李[^和]* |
李杰和李莲英和李二棍子 |
李杰李莲英李二棍子 |
表示匹配一个不是"和"的字符任意次 |
[d] |
456bdha3 |
4563 |
表示匹配任意一个数字,匹配到4个结果 |
[d]+ |
456bdha3 |
4563 |
表示匹配任意个数字,匹配到2个结果 |
分组 ()与 或 |[^]
身份证号码是一个长度为15或18个字符的字符串,如果是15位则全部?️数字组成,首位不能为0;如果是18位,则前17位全部是数字,末位可能是数字或x,下面我们尝试用正则来表示:
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
^[1-9]d{13,16}[0-9x]$ |
110101198001017032 |
110101198001017032 |
表示可以匹配一个正确的身份证号 |
^[1-9]d{13,16}[0-9x]$ |
1101011980010170 |
1101011980010170 |
表示也可以匹配这串数字,但这并不是一个正确的身份证号码,它是一个16位的数字 |
^[1-9]d{14}(d{2}[0-9x])?$ |
1101011980010170 |
False |
现在不会匹配错误的身份证号了()表示分组,将d{2}[0-9x]分成一组,就可以整体约束他们出现的次数为0-1次 |
^([1-9]d{16}[0-9x]|[1-9]d{14})$ |
110105199812067023 |
110105199812067023 |
表示先匹配[1-9]d{16}[0-9x]如果没有匹配上就匹配[1-9]d{14} |
转义符
在正则表达式中,有很多有特殊意义的是元字符,比如d和s等,如果要在正则中匹配正常的"d"而不是"数字"就需要对""进行转义,变成'\'。
在python中,无论是正则表达式,还是待匹配的内容,都是以字符串的形式出现的,在字符串中也有特殊的含义,本身还需要转义。所以如果匹配一次"d",字符串中要写成'\d',那么正则里就要写成"\\d",这样就太麻烦了。这个时候我们就用到了r'd'这个概念,此时的正则是r'\d'就可以了。
正则 |
待匹配字符 |
匹配结果 |
说明 |
---|---|---|---|
d |
d |
False |
因为在正则表达式中是有特殊意义的字符,所以要匹配d本身,用表达式d无法匹配 |
\d |
d |
True |
转义之后变成\,即可匹配 |
"\\d" |
'\d' |
True |
如果在python中,字符串中的''也需要转义,所以每一个字符串''又需要转义一次 |
r'\d' |
r'd' |
True |
在字符串之前加r,让整个字符串不转义 |
几个常用的非贪婪匹配
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
.*?的用法
. 是任意字符
* 是取 0 至 无限长度
? 是非贪婪模式。
何在一起就是 取尽量少的任意字符,一般不会这么单独写,他大多用在:
.*?x
就是取前面任意长度的字符,直到一个x出现
re模块下的常用方法
re.findall
返回所有满足匹配条件的结果,放在列表里
import re
ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果,放在列表里
print(ret)
ret1 = re.findall('d+', 'dfkhf4565fwef326wef315wef') # 匹配一个数字1次或者多次
print(ret1)
ret2 = re.findall('d', 'dfkhf4565fwef326wef315wef') # 匹配一个数字(单个)
print(ret2)
执行结果
['a', 'a']
['4565', '326', '315']
['4', '5', '6', '5', '3', '2', '6', '3', '1', '5']
re.search
函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None
import re
ret = re.search('a', 'eva egon yuan')
if ret:
print(ret.group()) # 从结果对象中获取结果
执行结果
a
为什么只有1个a呢?
search和findall的区别:
1 search找到一个就返回,findall是找所有
2 findall是直接返回一个结果的列表,search返回一个结果的对象
re.match
import re
ret = re.match('a', 'eva egon yuan')
print(ret)
执行结果
None
所有的match,意味着在正则表达式中添加了一个^,也就是'^a'的意思
import re
ret = re.match('e', 'eva egon yuan')
if ret:
print(ret.group())
执行结果
e
match
1 意味着在正则表达式中添加了一个^
2 和search一样 匹配返回结果对象,没匹配到返回None
3 和search一样 从结果中获取值 仍然用group()
re.split
先按'a'分割得到''和'bcd',在对''和'bcd'分别按'b'分割
import re
ret = re.split('[ab]', 'abcd')
print(ret)
结果
['', '', 'cd']
re.sub
将数字替换成'H',参数1表示只替换1个,如果不写1,则全部替换
import re
ret = re.sub('d', 'H', 'esdf7sam9tom', 1)
print(ret)
ret = re.sub('d', 'H', 'esdf7sam9tom')
print(ret)
执行结果
esdfHsam9tom
esdfHsamHtom
re.subn
将数字替换成'H',返回元组(替换的结果,替换了多少次)
import re
ret = re.subn('d', 'H', 'esdf7sam9tom')
print(ret)
执行结果
('esdfHsamHtom', 2)
re.compile
将正则表达式编译成为一个正则表达式对象,规则要匹配的是3个数字
正则表达式对象调用search,参数为待匹配的字符串
编译 在多次执行同一条正则规则的时候才适用
import re
obj = re.compile('d{3}')
ret = obj.search('abc123eeshds')
print(ret.group())
# 可以执行多个方法
ret1 = obj.match('999abc123e111ee888shds')
ret2 = obj.findall('123e111ee888shds')
print(ret1)
print(ret2)
执行结果
123
['123', '111', '888']
re.finditer
finditer返回一个存放匹配结果的迭代器
import re
ret = re.finditer('d', 'ds3sy4784a') #finditer返回一个存放匹配结果的迭代器
print(ret) # print(next(ret).group()) #查看第一个结果
print(next(ret).group()) #查看第二个结果
print([i.group() for i in ret]) #查看剩余的左右结果
执行结果
3
4
['7', '8', '4']
re.findall的优先级查询
import re
ret = re.findall('www.(baidu|soso).com', 'www.soso.com')
print(ret)
# findall会优先把匹配结果组里内容返回,如果想要匹配结果,取消权限即可
ret1 = re.findall('www.(?:baidu|soso).com', 'www.baidu.com')
print(ret1)
执行结果
['soso']
['www.baidu.com']
re.split的优先级查询
import re
ret = re.split('d+', 'sdsff41fef5fe45')
print(ret)
# 在匹配部分上()之后所切出的结果是不同的
# 没有()的没有保留所匹配的项,但是有()的却能够保留匹配的项
# 这个在某些需要保留匹配部分的使用过程中是非常重要的
ret1 = re.split('(d+)', 'sdsff41fef5fe45')
print(ret1)
执行结果
['sdsff', 'fef', 'fe', '']
['sdsff', '41', 'fef', '5', 'fe', '45', '']
匹配标签
分组命名和search遇到分组
有时候,不通过匹配周围的,无法匹配到想要的内容
分组的意义
1 对一组正则规则进行量词约束
2 从一整条正则规则匹配的结果中优先显示组内的内容
import re
ret = re.search("<(?Pw+)>w+", 'hello')
print(ret.group()) # hello 因为h1和结果的h2不同,所以不对
ret1 = re.search("<(?Pw+)>w+", 'hello')
print(ret1) # 匹配不到了返回值为None
ret2 = re.search("<(?Pw+)>w+", 'hello')
print(ret2.group()) # 匹配到了对应的内容
执行结果
练习题:
计算下面例子的结果(用正则去匹配)
s = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
完整代码
import re
def cal(exp):
"""计算乘除的值,返回一个str(float)"""
if '*' in exp:
a, b = exp.split('*')
return str(float(a) * float(b))
elif '/' in exp:
a, b = exp.split('/')
return str(float(a) / float(b))
def format(exp):
"""替换表达式中的多余符号"""
exp = exp.replace('++', '+')
exp = exp.replace('+-', '-')
exp = exp.replace('-+', '-')
exp = exp.replace('--', '+')
return exp
def dealwith(no_bracket_exp):
# 匹配乘除法
while True:
mul_div = re.search('d+(.?d+)?[*/]-?d+(.?d+)?', no_bracket_exp)
if mul_div:
exp = mul_div.group()
result = cal(exp)
no_bracket_exp = no_bracket_exp.replace(exp, result, 1)
else:break
no_bracket_exp = format(no_bracket_exp)
# 计算加减法
lst = re.findall(r'[-+]?d+(?:.d+)?', no_bracket_exp)
res = str(sum(float(i) for i in lst))
return res
def remove_bracket(s):
s = s.replace(' ', '') # 替换空格为空
while True:
ret = re.search(r'([^()]+)', s) # 匹配最内层的括号
if ret: # 能匹配到括号 就先处理括号内的加减乘除
no_bracket_exp = ret.group() # 拿到括号中的表达式
ret = dealwith(no_bracket_exp) # 把括号中的表达式交给的dealwith
s = s.replace(no_bracket_exp, ret, 1)
else: # 不能匹配到括号 就字节处理加减乘除
ret = dealwith(s) # 把表达式交给的dealwith
return ret
s = '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
print(remove_bracket(s))
执行结果
2776672.6952380957
- Linux 优化
- 基于重叠IO模型的 回显TCP服务器设计
- Git常用命令
- 手把手教你搭建SpringMVC——最小化配置
- Mysql-16-缓存的配置和使用
- 重叠(Overlapped)IO模型
- Mysql-15-mysql分布式应用
- 基于Spring Mvc实现的Excel文件上传下载
- Java程序员的日常—— Arrays工具类的使用
- Mysql-14-mysql的日志管理
- ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
- Java程序员的日常 —— static的用法讲解实践
- WSAEventSelect模型 ---应用实例,重写TCP服务器实例
- Mysql-13mysql的复制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Netty进阶之粘包和拆包问题
- 用侦察兵的故事趣讲ICMP和Ping,看完想忘都难!
- Kubernetes 使用 ceph-csi 消费 RBD 作为持久化存储
- ZooKeeper入门,看这篇就够了
- 超详细的RabbitMQ入门
- 用向量做Mantel的几个问题
- Eclipse集成Maven打包时报错:[ERROR] Unknown lifecycle phase "mvn". You must specify a valid lifecycle phase
- Windows下使用Nginx+Tomcat做负载均衡
- CTO 写的代码,真是绝了
- 网站克隆:setoolkit社工软件
- 什么是数据驱动测试?学习创建框架
- 自动化面试题,我用来面试成功了
- MySQL中的InnoDB是怎么解决幻读的?
- 整理了一些自己可能会用到的R包
- Service Worker初探