正则表达式与re模块
一、正则表达式
什么是正则表达式
- 一套规则———匹配字符串的
能做什么
- 检测一个输入的字符串是否合法——web开发项目 表单验证
- 用户输入一个内容的时候,我们要提前做检测
- 能够提高程序的效率并且减轻服务器的压力
- 从一个大文件中找到所有符合规则的内容——日志分析或爬虫
- 能够高效的从一大段文字中快速找到符合规则的内容
二、正则规则
所有的规则中的字符就可以刚好匹配到字符串中的内容
字符组 [ ] :描述的是一个位置上能出现的所有可能性
- 接受范围,可以描述多个范围,连着写就可以了
- [abc]:一个中括号只表示一个字符位置
- 匹配a或b或者c
- [0-9]:根据ASCII进行范围的比对
- [a-z]:匹配a到z范围内的所有小写字母
- [A-Z]:匹配A到Z范围内的所有大写字母
- [a-zA-z]:匹配所有大小写字母
- [0-9a-z]:匹配0到9和所有的小写字母
- [0-9a-zA-Z_]:匹配所有数字大小写字母和下划线
在正则表达式中能够帮助我们表示匹配的内容的符号都是正则中的 元字符
- [0-9] ———> \d:表示匹配一位任意数字(digit)
- [0-9a-zA-Z_]———> \w:表示匹配数字字母下划线(word)
- 空格 ———> 就是空格
- tab ———> \t:匹配tab空白
- enter回车 ———> \n:匹配回车
- 空格,tab和回车———> \s:表示匹配所有空白包括空格tab和回车
元字符——匹配内容的规则
-
[]
:——匹配字符组内的一个字符 -
[^]
:——除了字符组内的内容都匹配 -
\d
:——匹配所有的数字 -
\D 和 [^\d]
:——匹配所有非数字(大写D) -
\w
:——匹配所有的字母 -
\W 和 [^\w]
:——匹配所有的非字母(大写W) -
\s
:——匹配所有的空白 -
\S
:——匹配所有的非空白(大写S) -
[\d\D]、[\w\W]、[\s\S]
:——表示匹配所有 -
.
——匹配除了执行换行符之外的所有 -
^
:——匹配一个字符串的开始比如(^\d)匹配以数字开头的 -
$
:——匹配一个字符串的结尾比如(\d$)匹配以数字结尾的 -
a表达式|b表达式
:——匹配a或者b表达式中的内容,如果a匹配成功了,不会继续和b匹配,所以,如果两个规则有重叠部分,总是把长的放在前面 -
()
:——分组,约束或描述的内容的范围问题-
匹配 www.baidu.com 或 www.oldboy.com 或 www.jd.com 方法一:www\.oldboy\.com|www\.baidu\.com|www\.jd\.com 方法二:www\.(oldboy|baidu|jd)\.com
-
三、量词
- {n}:——表示匹配n次
- {n,}:——表示匹配至少n次
- {n,m}:——表示至少匹配n次,最多匹配m次
- ?:——表示匹配 0 次或 1 次
- +:——表示 1 次或多次
- *:——表示 0 次或多次
匹配0次:
- 整数:
\d+
- 小数:
\d+\.\d+
- 整数或小数:
\d+\.?\d*
(23423.)也会匹配上 - 分组的作用匹配小数或整数:
\d+(\.d+)?
手机号码:
- 1 3-9 11位
1[3-9]\d{9}
- 第一位是1开头,第二位是3-9范围的任意数字后面是9位数字
判断用户输入的内容是否合法,如果用户输入的对就能查到结果,如果输入的不对就不能查到结果
- 加上首尾匹配元字符:就会固定匹配一个满足条件的字符
^1[3-9]\d{9}$
贪婪匹配:
- 在量词允许的情况下,尽量多的匹配内容
.*x
:表示匹配任意字符,任意多次数,遇到最后一个x才停下来
非贪婪(惰性)匹配
.*?x
:表示匹配任意字符,任意多次数,但是一旦遇到x就停下来- 在量词后加?号就会变成惰性匹配
转义符
- 原本有特殊意义的字符,到了表达它本身的意义的时候,需要转义
- 有一些有特殊意义的内容,放在字符组中,会取消它的特殊意义
[().*+?]
——所有的内容放在字符组中会取消它的特殊意义[a\-c]
:——在字符组中 - 表示范围,如果不希望它表示范围,需要转义,或者放在字符组的最前面或最后面
四、re模块
-
re.findall ('表达式','需要匹配的内容') :
- 以字典的形式返回匹配到的所有字符串
import re ret = re.findall('\d+','123abc456def') print(ret) # 输出 ['123', '456']
- findall:遇到分组的时候,会按照完整的正则进行匹配,只是显示括号里匹配到的内容
- 取所有符合条件的,优先显示分组中的
ret = re.findall('2(\d)\d','1234abcd4267efgh') print(ret) # 只会显示匹配到234和267的第二个字符 # 输出 ['3', '6']
-
re.search('表达式','需要匹配的内容'):
- 匹配第一个满足条件到的字符返回的是一个变量,只能用group()查看。
ret = re.search('\d+','123abc456def') print(ret) # 返回的是变量 if ret: # 匹配到了才显示,没匹配到返回 None,所以做了个判断 print(ret.group()) # 输出 <re.Match object; span=(0, 3), match='123'> 123
- search:还是按照完整的正则进行匹配,显示也显示匹配到的第一个内容,但我们可以通过给group方法传参数来获取具体元组的内容
- 只取第一个符合条件的,没有优先显示这件事儿
- 得到的结果是一个变量:
- 变量 .group() 的结果 完全和 变量 .group(0) 的结果一致
- 变量 .group(n) 的形式来指定获取第 n 个分组中匹配到的内容
ret = re.search('2(\d)(\d)','1234abcd4267efgh') print(ret) # 变量 if ret: print(ret.group()) print(ret.group(1)) print(ret.group(2)) # 输出 <re.Match object; span=(1, 4), match='234'> 234 3 4
-
为什么在search中不需要分组优先,而在findall中需要?
- 在 findall中加上分组括号,是为了对真正需要的内容进行提取
ret = re.findall('<\w+>(\w+)</\w+>','<h1>abcd12345678efgh</h1>') print(ret) # 输出 ['abcd12345678efgh']
- 在search中
ret = re.search('<(\w+)>(\w+)</\w+>','<h1>abcd123678efgh</h1>') print(ret.group()) # 获取所有匹配到的内容 print(ret.group(1)) # 获取第一个分组的内容 print(ret.group(2)) # 获取第二个分组的内容 # 输出 <h1>abcd12345678efgh</h1> h1 abcd12345678efgh
-
为什么要用分组,以及 findall 的分组优先到底有什么好处
- 使用findall分组优先可以把想要的内容放在分组里面
- 使用search分组就可以在匹配完了之后对自己想要操作的分组进行操作
exp = '2-3*(5+6)' ret = re.search('(\d+)[+](\d+)',exp) print(ret) # 获取a+b或a-b,并且计算他们的结果。 print(ret.group(1)) print(ret.group(2)) print(int(ret.group(1)) + int(ret.group(2)))
-
如何取消分组优先:
- 如果在写正则的时候由于不得已的原因,导致不要的内容也写在分组里
-
(?:)——取消这个分组的优先显示
ret = re.findall('<(?:\w+)>(\w+)</\w+>','<h1>abcd12345678efgh</h1>') print(ret) # 输出 ['abcd12345678efgh']
-
split:以匹配的字符为截短(加上分组可以显示分组内容)
import re ret = re.split('\d+','alex222wusir') print(ret) # 输出 ['alex', 'wusir'] ---------------------------------------- import re # 加上分组会显示分组的内容 ret = re.split('\d(\d)\d', 'alex123wusir') print(ret) # 输出 ['alex', '2', 'wusir']
-
sub('匹配需要替换内容表达式','替换的新内容',替换次数):替换匹配到的内容
import re ret = re.sub('\d+','HH','aa33bb44cc') print(ret) # 输出 aaHHbbHHcc -------------------------------------- import re ret = re.sub('\d+','HH','aa33bb44cc',1) # 替换1次 print(ret) # 输出 aaHHbb44cc
-
subn:和sub一样,不过以元组的形式返回替换之后的的内容和替换的次数
import re ret = re.subn('\d+','H','aa33bb44cc') print(ret) # 输出 ('aaHbbHcc', 2)
-
match:在表达式之前默认加了个 ^ 元字符(匹配以什么开头)
import re # 表达式相当于='^\d+'匹配任意一个或多个数字开头的字符 ret = re.match('\d+','1234abcd567') print(ret.group()) # 输出 1234
-
compile:节省代码时间的工具,没有重复使用同一个正则,也不能节省时间。
- 假如同一个正则表达式要被多次使用
- 节省了多次解析同一个正则表达式的时间
import re ret = re.compile('\d+') res1 = ret.search('abcd1234') res2 = ret.findall('abcd1234') print(res1) print(res2) # 输出 <re.Match object; span=(4, 8), match='1234'> ['1234']
-
finditer:返回的是一个迭代器,为了节省空间
import re ret = re.finditer('\d+','agks1ak018093') print(ret) # 是一个迭代器 for i in ret: # 把匹配出来的结果循环出来,节省了空间的占用 print(i.group()) # 输出 <callable_iterator object at 0x00000204E6721E20> 1 018093
五、分组命名
-
标准格式
(?P<组名>正则表达式)
-
有的时候我们要匹配的内容是包含在不想要的内容之中的
- 只能先把不想要的内容匹配出来,然后再想办法从结果中去掉
import re
exp = '2014-9-23和1999-04-30'
ret = re.search(r"[1-9]\d{3}-(?P<month>[1][0-2]|0?[1-9])-(?P<date>[[1-2][0-9]|[3][01]|0?[1-9])", exp)
print(ret.group())
print(ret.group('month'))
print(ret.group('date'))
# 输出
2014-9-23
9
23
本文来自博客园,作者:Mr-Yang`,转载请注明原文链接:https://www.cnblogs.com/XiaoYang-sir/p/14713297.html