本文主要内容:正则表达式概述、re模块操作、表示字符、原始字符串、表示数量、表示边界、 匹配分组、re模块的高级用法、贪婪和非贪婪、小练习
0x01 正则表达式概述
正则表达式,又称正规表示式、正规表示法、正规表达式、 规则表达式、 常规表示法(英语:Regular Expression,在代码中常简写为regex、 regexp或RE),是计算机科学的一个概念。正则表达式使用单个字符串来描述、 匹配一系列匹配某个句法规则的字符串。 在很多⽂本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。
Regular Expression的“Regular”⼀般被译为“正则”、 “正规”、 “常规”。 此处的“Regular”即是“规则”、 “规律”的意思, Regular Expression即“描述某种规则的表达式”之意。
0x02 re模块操作
在Python中需要通过正则表达式对字符串进⾏匹配的时候,可以使⽤⼀个模
块,名字为re
2.1 re模块的使用过程
1 2 3 4 5 6 7 8
| import re
result = re.match(正则表达式,要匹配的字符串)
result.group()
|
re.match是进行正则匹配检查的方法,若字符串匹配正则表达式,则match方法返回匹配对象(Match Object),否则返回None(注意不是空字符串””)
匹配对象Match Object对象具有group方法,用来返回字符串的匹配部分
2.2 re模块示例
1 2 3 4 5
| import re result = re.match("hello","hello world")
result
|
Out:<re.Match object; span=(0, 5), match='hello'>
Out:'hello'
0x03 表示字符
正则表达式单字符匹配常用的模式
字符 |
功能 |
. |
匹配任意1个字符(除了\n) |
[] |
匹配[]中列举的字符 |
\d |
匹配数字,即0-9 |
\D |
匹配非数字,即不是数字 |
\s |
匹配空白,即空格、tab键 |
\S |
匹配非空白 |
\w |
匹配单词字符,即a-z、A-Z、0-9、_ |
\W |
匹配非单词字符 |
3.1 示例1:.
1 2 3 4
| import re ret1 = re.match(".","a")
ret1.group()
|
Out:'a'
1 2
| ret2 = re.match(".","bca") ret2.group()
|
Out:'b'
1 2
| ret3 = re.match(".","@18") ret3.group()
|
Out:'@'
3.2 示例2:[]
1 2 3
| ret1 = re.match("h","hello Python") ret1.group()
|
Out:'h'
1 2 3
| ret2 = re.match("H","Hello Python") ret2.group()
|
Out:'H'
1 2 3
| ret3 = re.match("[hH]","hello Python") ret3.group()
|
Out:'h'
1 2
| ret4 = re.match("[hH]","Hello Python") ret4.group()
|
Out:'H'
1 2 3
| ret5 = re.match("[0123456789]","7Hello") ret5.group()
|
Out:'7'
1 2 3
| ret6= re.match("[0-9]","7Hello") ret6.group()
|
Out:'7'
3.3 示例3:\d
1 2 3
| ret1 = re.match("hello2","hello2world") ret1.group()
|
Out:'hello2'
1 2 3
| ret2 = re.match("hello\d","hello2world") ret2.group()
|
Out:'hello2'
0x04 原始字符串
4. 1 各种示例
打印:dd
打印:\ndd
Out:'c:\\a\\b\\c'
打印:c:\a\b\c
1 2
| re.match("c:\\\\",mm).group()
|
Out:'c:\\'
1 2
| ret = re.match("c:\\\\a",mm).group() print(ret)
|
打印:c:\a
1 2 3
| ret =re.match(r"c:\\a",mm).group() print(ret)
|
打印:c:\a
1 2
| ret = re.match(r"c:\a",mm).group() print(ret)
|
打印:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-45-1a051863b698> in <module>
----> 1 ret = re.match(r"c:\a",mm).group()
2 print(ret)
AttributeError: 'NoneType' object has no attribute 'group'
4.2 示例说明
Python字符串前面加上r 表示原生字符串
与大多数编程语言相同,正则表达式里使用”\n”作为转义字符,这就可能造成反斜杠困扰。加入匹配文本中的字符串”\“,那么使用编程语言表示的正则表达式里将需要4个反斜杠“\\”:前两个和后两个分别用于在编程语言里转义反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠
Python里的原生字符串很好的解决了这个问题,不再担心漏写反斜杠,写出的表达式更直观
1 2 3
| mm= "c:\\a\\b\\c" ret = re.match(r"c:\\a",mm).group() print(ret)
|
打印:c:\a
0x05 表示数量
匹配多个字符的相关格式
字符 |
功能 |
* |
匹配前一个字符出现0次或者无限次,即可有可无 |
+ |
匹配前一个字符出现1次或者无限次,即至少有1次 |
? |
匹配前一个字符出现1次或者0次, 即要么有1次,要么没有 |
{m} |
匹配前一个字符出现m次 |
{m,} |
匹配前一个字符至少出现m次 |
{m,n} |
匹配前一个字符出现从m到n次 |
5.1 示例1:*
需求:匹配出,一个字符串第一个字母为大写字符,后面都是小写字母并且这些小写字母可有可无
1 2 3
| import re ret = re.match("[A-Z][a-z]*","Mm") ret.group()
|
Out:'Mm'
1 2 3
| ret = re.match("[A-Z][a-z]*","M") ret.group()
|
Out:'M'
1 2 3
| ret = re.match("[A-Z][a-z]*","Mabcdef") ret.group()
|
Out:'Mabcdef'
5.2 示例2:+
需求:匹配出,变量名是否有效
1 2 3
| ret = re.match("[a-zA-Z]+[\w]*","name1") ret.group()
|
Out:'name1'
1 2
| ret = re.match("[a-zA-Z_]+[\w]*","_name") ret.group()
|
Out:'_name'
1 2
| ret = re.match("[a-zA-Z_]+[\w]*","2_name") ret.group()
|
打印:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-65-572ee7f143b4> in <module>
1 ret = re.match("[a-zA-Z_]+[\w]*","2_name")
----> 2 ret.group()
AttributeError: 'NoneType' object has no attribute 'group'
5.3 示例3:?
需求:匹配出,0-99之间的数字
1 2
| ret = re.match("[1-9]?[0-9]","7") ret.group()
|
Out:'7'
1 2
| ret = re.match("[1-9]?[0-9]","33") ret.group()
|
Out:'33'
1 2 3
| ret =re.match("[1-9]?[0-9]","09") ret.group()
|
Out:'0'
5.4 示例4:{m}
需求:匹配出,8-20位的密码,可以大小写英文字母、数字、下划线
1 2 3 4
| import re
ret =re.match("[a-zA-Z0-9_]{6}","12a3g45678") ret.group()
|
Out:'12a3g4'
1 2
| ret = re.match("[a-zA-Z0-9_]{8,20}","1ad12fffs39d739473920_d398sd") ret.group()
|
Out:'1ad12fffs39d73947392'
1 2 3
| ret = re.match("[\w]{8,20}","1ad12fffs39d739473920_d398sd") ret.group()
|
Out:'1ad12fffs39d73947392'
- 简单练习,匹配出163的邮箱地址,且@符号之前有4-20位,例如
hello@163.com
1 2 3
| ret =re.match("[\w]{4,20}@163\.com","hell0@163.com") ret.group()
|
Out:'hell0@163.com'
1 2 3
| ret = re.match("[\w]{4,20}@163\.com","hello@163.comddddd") ret.group()
|
Out:'hello@163.com'
0x06 表示边界
字符 |
功能 |
^ |
匹配字符串开头 |
$ |
匹配字符串结尾 |
\b |
匹配一个单词的边界 |
\B |
匹配非单词边界 |
6.1 示例1:$
需求:匹配163.com的邮箱地址
1 2 3 4 5
| import re
ret = re.match("[\w]{4,20}@163\.com","xiaoWang@163.com") ret.group()
|
Out:'xiaoWang@163.com'
1 2 3
| ret = re.match("[\w]{4,20}@163\.com","xiaoWang@163.comheihhh") ret.group()
|
Out:'xiaowang@163.com'
1 2 3
| ret = re.match("[\w]{4,20}@163\.com$","xiaoWang@163.comheihhh") ret.group()
|
打印:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-93-83efc35bff7d> in <module>
1 # 通过$来确定末尾
2 ret = re.match("[\w]{4,20}@163\.com$","xiaoWang@163.comheihhh")
----> 3 ret.group()
AttributeError: 'NoneType' object has no attribute 'group'
6.2 示例2:\b
1
| re.match(r".*\bver\b","ho ver abc").group()
|
Out:'ho ver'
1 2
| re.match(r".*\bver\b","ho verabc").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-95-d65bbce81439> in <module>
----> 1 re.match(r".*\bver\b","ho verabc").group()
AttributeError: 'NoneType' object has no attribute 'group'
1 2
| re.match(r".*\bver\b","hover abc").group()
|
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-96-53f69a15373e> in <module>
----> 1 re.match(r".*\bver\b","hover abc").group()
AttributeError: 'NoneType' object has no attribute 'group'
6.3 示例3:\B
1
| re.match(r".*\Bver\B","hoverabc").group()
|
Out:'hover'
1 2
| re.match(r".*\Bver\B","ho verabc").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-98-a9d342249fa1> in <module>
1 # 当指定词有左边界时,出错
----> 2 re.match(r".*\Bver\B","ho verabc").group()
AttributeError: 'NoneType' object has no attribute 'group'
1 2
| re.match(r".*\Bver\B","hover abc").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-99-aae06b891466> in <module>
----> 1 re.match(r".*\Bver\B","hover abc").group()
AttributeError: 'NoneType' object has no attribute 'group'
1 2
| re.match(r".*\Bver\B","ho ver abc").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-101-9630adaf466f> in <module>
1 # 当指定的词,左右都有边界时,\B出错
----> 2 re.match(r".*\Bver\B","ho ver abc").group()
AttributeError: 'NoneType' object has no attribute 'group'
0x07 匹配分组
字符 |
功能 |
|
|
(ab) |
将括号中字符作为一个分组 |
\num |
引用分组num匹配到的字符串 |
(?P<name> ) |
分组起别名 |
(?P=name) |
引用别名为name分组匹配到的字符串 |
7.1 示例1:|
需求:匹配出0-100之间的数字
1 2 3
| import re ret = re.match("[1-9]?\d","8") ret.group()
|
Out:'8'
1
| re.match("[1-9]?\d","78").group()
|
Out:'78'
1 2
| re.match("[1-9]?\d","08").group()
|
Out:'0'
1 2
| re.match("[1-9]?\d$","08").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-106-70c347cddb39> in <module>
1 # 修正之后的
----> 2 re.match("[1-9]?\d$","08").group()
AttributeError: 'NoneType' object has no attribute 'group'
可匹配0到100(包括0和100)
1 2
| re.match("[1-9]?\d$|100","100").group()
|
Out:'100'
1
| re.match("[1-9]?\d$|100","0").group()
|
Out:'0'
7.2 示例2:()
需求:匹配出163、126、qq邮箱直接的数字
1
| re.match("\w{4,20}@163\.com","test@163.com").group()
|
Out:'test@163.com'
1 2
| re.match("\w{4,20}@(163|126|qq)\.com","test@126.com").group()
|
Out:'test@126.com'
1
| re.match("\w{4,20}@(163|126|qq)\.com","test@gmail.com").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-111-d286dbf8fcf2> in <module>
----> 1 re.match("\w{4,20}@(163|126|qq)\.com","test@gmail.com").group()
AttributeError: 'NoneType' object has no attribute 'group'
1 2
| ret = re.match("([^-]*)-(\d+)","010-12345678") ret.group()
|
Out:'010-12345678'
Out:('010', '12345678')
Out:'010'
Out:'12345678'
7.3 示例3:\
需求:匹配出<html>hh</html>
1 2
| re.match("<[a-zA-Z]*>\w*</[a-zA-Z]*>","<html>hh</html>").group()
|
Out:'<html>hh</html>'
1 2
| re.match("<[a-zA-Z]*>\w*</[a-zA-Z]*>", "<html>hh</htmlbalabala>").group()
|
Out:'<html>hh</htmlbalabala>'
- 思路:如果在第一对<>中是什么,按理说后面的那对<>中就应该是什么
1 2
| re.match(r"<([a-zA-Z]*)>\w*</\1>","<html>hh</html>").group()
|
Out:'<html>hh</html>'
1 2
| re.match(r"<([a-zA-Z]*)>\w*</\1>","<html>hh</htmlddd>").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-126-f2f15b62f076> in <module>
1 # 因为2对<>中的数据不一致,所以没匹配出来
----> 2 re.match(r"<([a-zA-Z]*)>\w*</\1>","<html>hh</htmlddd>").group()
AttributeError: 'NoneType' object has no attribute 'group'
7.4 \number
需求:匹配出<html><h1>www.baidu.com</h1></html>
1
| re.match(r"<(\w*>)<(\w*)>.*</\2></\1","<html><h1>www.baidu.com</h1></html>").group()
|
Out:'<html><h1>www.baidu.com</h1></html>'
1
| re.match(r"<(\w*>)<(\w*)>.*</\2></\1","<html><h1>www.baidu.com</h2></html>").group()
|
Out:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-128-1cf071943a2d> in <module>
----> 1 re.match(r"<(\w*>)<(\w*)>.*</\2></\1","<html><h1>www.baidu.com</h2></html>").group()
AttributeError: 'NoneType' object has no attribute 'group'
7.5 示例5:(?P<name>)(?P=name)
1 2
| ret = re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.itcast.cn</h1></html>") ret.group()
|
Out:'<html><h1>www.itcast.cn</h1></html>'
- 注意:
(?P<name>)和(?P=name)
中的字母p大写
0x08 re模块的高级用法
search
需求:匹配出文章的阅读次数
1 2
| ret =re.search(r"\d+","阅读次数为9999") ret.group()
|
Out:'9999'
findall
需求:统计出python、c、c++相应文章阅读的次数
1 2
| ret = re.findall(r"\d+","python=9999,c=7890,c++=1234") ret
|
Out:['9999', '7890', '1234']
sub
将匹配到的数据进行替换
需求:将匹配到的阅读次数加1
1 2 3
| ret =re.sub(r"\d+","998","python=997") ret
|
OUt:'python=998'
1 2 3 4 5 6 7 8 9 10
| import re def add(temp): print(temp) strNum = temp.group() num =int(strNum) +1 return str(num)
ret =re.sub(r"\d+",add,"python=997") ret
|
Out:<re.Match object; span=(7, 10), match='997'>
Out:'python=998'
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div> <p>岗位职责: </p> <p>完成推荐算法、 数据统计、 接⼝、 后台等服务器端相关⼯作</p> <p><br></p> <p>必备要求: </p> <p>良好的⾃我驱动⼒和职业素养, ⼯作积极主动、 结果导向</p> <p> <br></p> <p>技术要求: </p> <p>1、 ⼀年以上 Python 开发经验, 掌握⾯向对象分析和设计, 了解设计模式</p > < p>2、 掌握HTTP协议, 熟悉MVC、 MVVM等概念以及相关WEB开发框架</p> <p>3、 掌握关系数据库开发设计, 掌握 SQL, 熟练使⽤ MySQL/PostgreSQL 中 的⼀种<br></p> <p>4、 掌握NoSQL、 MQ, 熟练使⽤对应技术解决⽅案</p>
|
1 2 3 4 5 6 7 8 9 10 11 12
| s="""<div> <p>岗位职责:</p> <p>完成推荐算法、 数据统计、 接口、 后台等服务器端相关工作</p> <p><br></p> <p>必备要求: </p> <p>良好的自我驱动力和职业素养, 工作积极主动、 结果导向</p> <p> <br></p> <p>技术要求: </p> <p>1、 一年以上 Python 开发经验, 掌握面向对象分析和设计, 了解设计模式</p> """ ret =re.sub(r"</?\w*>","",s) ret
|
Out:
'\n岗位职责:\n完成推荐算法、 数据统计、 接口、 后台等服务器端相关工作\n\n必备要求: \n良好的自我驱动力和职业素养, 工作积极主动、 结果导向\n \n技术要求: \n1、 一年以上 Python 开发经验, 掌握面向对象分析和设计, 了解设计模式 \n'
split
根据匹配进行切割字符串,并返回一个列表
需求:切割字符串“info:dddd 33 shandong”
1 2
| ret =re.split(r":| ","info:dddd 33 shandong") ret
|
Out:['info', 'dddd', '33', 'shandong']
0x09 Python贪婪和非贪婪
Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;
非贪婪相反,总是尝试匹配尽可能少的字符。
在*
、?
、+
、{m,n}
后面加上?
,使贪婪变成非贪婪
1 2 3
| s = "this is a number 234-235-22-423" r = re.match(".+(\d+-\d+-\d+-\d+)",s) r.group(1)
|
Out:'4-235-22-423'
1 2 3
| r = re.match(".+?(\d+-\d+-\d+-\d+)",s) r.group(1)
|
Out:'234-235-22-423'
说明:
正则表达式模式中使用到通配字, 那它在从左到右的顺序求值时, 会尽量“抓取”满足匹配最长字符串, 在我们上面的例子里面, “.+”会从字符串的起始处抓取满足模式的最长字符, 其中包括我们想得到的第一个整型字段的中的大部分,“\d+”只需一位字符就可以匹配, 所以它匹配了数字“4”, 而“.+”则匹配了从字符串起始到这个第一位数字4之前的所有字符。
解决方式:非贪婪操作符“?”,这个操作符可以用在*
、?
、+
、{m,n}
后面,要求正则匹配的越少越好。
1
| re.match(r"aa(\d+)","aa2343ddd").group(1)
|
Out:'2343'
1
| re.match(r"aa(\d+?)","aa2343ddd").group(1)
|
Out:'2'
练习1
提取下面文本中图片链接
1
| <link rel="apple-touch-icon-precomposed" href="https://s.mozhe.cn/static/ico/apple-touch-icon.png">
|
1 2 3
| s= """<link rel="apple-touch-icon-precomposed" href="https://s.mozhe.cn/static/ico/apple-touch-icon.png"> <link rel="apple-touch-icon-precomposed" href="https://s.mozhe.cn/static/ico/apple-touch-icon.png">""" ret =re.search(r"https:.+?\.png",s) ret.group()
|
Out:'https://s.mozhe.cn/static/ico/apple-touch-icon.png'
练习2
1 2 3
| https://www.baidu.com/s?wd=dd&rsv_spt=1 正则后变为: https://www.baidu.com/
|
1 2
| s= """https://www.baidu.com/s?wd=dd&rsv_spt=1""" re.sub(r"https://.+?/", "", s)
|
Out:'s?wd=dd&rsv_spt=1'
1 2
| re.sub(r"(https://.+?/).*",lambda x: x.group(1),s)
|
Out:'https://www.baidu.com/'