正则表达式
1. 正则表达式概述
- 正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。
- Regular Expression的“Regular”一般被译为“正则”、“正规”、“常规”。此处的“Regular”即是“规则”、“规律”的意思,Regular Expression即“描述某种规则的表达式”之意。
2. 模块操作
在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个模块,名字为re
2.1 re模块的使用过程
# 导入re模块
import re
# 使用match方法进行匹配操作
result = re.match(正则表达式,要匹配的字符串)
# 如果上一步匹配到数据的话,可以使用group方法来提取数据
result.group()
- **re.match **
查询标志
大部分查询方法还可以接受一个查询标志参数。查询标志让正则表达式具有不同的行为。下面一一说明。
标志 | 作用 |
---|---|
re.A、 re.ASCII |
以ASCII模式查询,默认是Unicode模式 |
re.DEBUG | 显示编译表达式的调试信息 |
re.I、re.IGNORECASE | 忽略字母的大小写 |
re.L、re.LOCALE | 以区域敏感方式查询匹配 |
re.M、re.MULTILINE | 开启多行模式,开启之后行边界符^$会匹配每行的开始和结束,而不是整个字符串的开始和结束 |
re.S、re.DOTALL | 使用此标志,会让点符号匹配所有字符,默认情况下点符号会匹配换行符以外的符号 |
re.X、re.VERBOSE | 开启啰嗦模式,可以在写正则表达式的时候添加注释 |
re.match
是用来进行正则匹配检查的方法,若字符串匹配正则表达式,则match方法返回匹配对象(Match Object),否则返回None(注意不是空字符串"")。
匹配对象Macth Object具有group方法,用来返回字符串的匹配部分。
2.2re模块示例(匹配以taobao开头的语句)
>>>import re
>>>re.match('taobao', 'taobao.com')
<_sre.SRE_Match object; span=(0, 6), match='taobao'>
>>>re.match('taobao', 'taobao.com').group()
'taobao'
说明
- re.match() 能够匹配出以xxx开头的字符串,是从第一个字符开始匹配的
3. 表示字符
字符 | 功能 |
---|---|
中文 | [\u4e00-\u9fa5] |
. | 匹配任意一个字符(除了\n) |
[ ] | 匹配[ ]中列举的一个字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格,tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_,还有各个国家的文字 |
\W | 匹配非单词字符 |
- .匹配任意一个字符(除了\n)
>>>import re
>>>re.match('.','abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>re.match('.','abc').group()
'a'
>>>re.match('.','\nabc')
>>>re.match('.','\tabc')
<_sre.SRE_Match object; span=(0, 1), match='\t'>
>>>re.match('.','\tabc').group()
'\t'
匹配[ ]中列举的一个字符
>>>import re
>>>re.match("[hH]","hello Python")
<_sre.SRE_Match object; span=(0, 1), match='h'>
>>>re.match("[hH]","Hello Python")
<_sre.SRE_Match object; span=(0, 1), match='H'>
>>>re.match("[0-9]","7Hello Python")
<_sre.SRE_Match object; span=(0, 1), match='7'>
\w 匹配单词字符,即a-z、A-Z、0-9、_,还有各个国家的文字。加上第三个参数re.ASCII
就是以ASCII码匹配,就只能匹配字母、数字、下划线
>>>import re
>>>re.match('\w','かきく')
<_sre.SRE_Match object; span=(0, 1), match='か'>
>>>re.match('\w','ㅈㅊㅅ')
<_sre.SRE_Match object; span=(0, 1), match='ㅈ'>
>>>re.match('\w','汉字')
<_sre.SRE_Match object; span=(0, 1), match='汉'>
>>>re.match('\w','abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>re.match('\w','汉字',re.ASCII)#这个就没有匹配成功
>>>re.match('\w','a汉字',re.ASCII)
<_sre.SRE_Match object; span=(0, 1), match='a'>
4. 原始字符串
在很多编程语言中,由于有转义字符这么一种东西的存在,导致正则表达式需要使用两个斜杠来处理。
- Python中字符串前面加上 r 表示原生字符串,
- 与大多数编程语言相同,正则表达式里使用""作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"",那么使用编程语言表示的正则表达式里将需要4个反斜杠 "\" :前 两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
- Python里的原生字符串很好地解决了这个问题,有了原始字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
>>>import re
>>>m = "c:\\a\\b\\c"
>>>m
'c:\\a\\b\\c'
>>>print(m)
c:\a\b\c
>>>re.match("c:\\\\",m).group()
'c:\\'
>>>print(re.match("c:\\\\",m).group())
c:\
>>>re.match(r"c:\\a",m).group()
'c:\\a'
>>>print(re.match(r"c:\\a",m).group())
c:\a
5. 表示数量
匹配多个字符的相关格式
字符 | 功能 |
---|---|
* | 匹配前面的子表达式任意次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即最少有一次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m 次 |
{m,n} | 匹配前一个字符出现从m到n次 |
示例1 *
匹配出,一个字符串第一个字母为大小字符,后面都是小写字母并且这些小写字母可有可无
>>>import re
>>>re.match("[A-Z][a-z]*","Aabcdef")
<_sre.SRE_Match object; span=(0, 7), match='Aabcdef'>
示例2:?
需求:匹配出,0到99之间的数字
>>>import re
re.match("[1-9]?[0-9]","33")
>>><_sre.SRE_Match object; span=(0, 2), match='33'>
>>>re.match("[1-9]?[0-9]","09")
<_sre.SRE_Match object; span=(0, 1), match='0'>
>>>re.match("[1-9]?[0-9]","9")
<_sre.SRE_Match object; span=(0, 1), match='9'>
示例3:{m,n}
需求:匹配出,8到20位的密码,可以是大小写英文字母、数字、下划线
>>>import re
>>>re.match("[a-zA-Z0-9_]{8,20}","1ad12f23s34455ff66")
<_sre.SRE_Match object; span=(0, 18), match='1ad12f23s34455ff66'>
示例4:
匹配出163的邮箱地址,且@符号之前有4到20位,例如hello@163.com
import re
print('6~18个字符,可使用字母、数字、下划线,需以字母开头')
while True:
address=input('>>>输入邮箱地址:')
ret=re.match('[a-zA-Z]\w{3,19}@163\.com',address)
if ret == None:
print('输入有误,请重新输入。邮箱格式为6~18个字符,可使用字母、数字、下划线,需以字母开头')
continue
else:
ret = ret.group()
print('您的邮箱地址:%s'%ret)
break
运行结果
G:\tools\python3.5\python.exe E:/workspace/day29/练习1.py
6~18个字符,可使用字母、数字、下划线,需以字母开头
>>>输入邮箱地址:haha
输入有误,请重新输入。邮箱格式为6~18个字符,可使用字母、数字、下划线,需以字母开头
>>>输入邮箱地址:haha@163.com
您的邮箱地址:haha@163.com
6. 表示边界
字符 | 功能 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界 |
示例 $
>>>import re
>>>re.match("[\w]{4,20}@163\.com", "xiaoWang@163.comheihei")
<_sre.SRE_Match object; span=(0, 16), match='xiaoWang@163.com'>
>>>re.match("[\w]{4,20}@163\.com$", "xiaoWang@163.comheihei")
>>>re.match("[\w]{4,20}@163\.com", "xiaoWang@163.comheihei")
<_sre.SRE_Match object; span=(0, 16), match='xiaoWang@163.com'>
7. 匹配分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P<name>) | 分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
示例 |
>>>import re
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@gmail.com")
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com")
<_sre.SRE_Match object; span=(0, 11), match='test@qq.com'>
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com").group(1)
'qq'
示例 \number
>>>import re
>>>re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.taobao.com</h1></html>")
<_sre.SRE_Match object; span=(0, 36), match='<html><h1>www.taobao.com</h1></html>'>
>>>re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.taobao.com</h2></html>")
第二个匹配不到是后面的\2
引用了分组2的值(h1
),与字符串中的‘h2’不匹配,所以返回None
示例:(?P<name>) (?P=name)
>>>import re
>>>re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.taobao.com</h1></html>")
<_sre.SRE_Match object; span=(0, 36), match='<html><h1>www.taobao.com</h1></html>'>
>>>re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.taobao.com</h2></html>")
注意:
(?P<name>)和(?P=name)中的字母p大写
8. re模块的高级用法
1.search()
我们在前面提到过match()方法是从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了。
import re
s='Hello World 123abc456'
result=re.match('World.*abc',s)
print(result)
result=re.search('World.*abc',s)
print(result)
运行结果
None
<_sre.SRE_Match object; span=(6, 18), match='World 123abc'>
在这里我们有一个字符串,它是以Hello开头的,但是正则表达式我们是以World开头的,整个正则表达式是字符串的一部分,但是这样匹配是失败的,也就是说只要第一个字符不匹配整个匹配就不能成功。
所以match()
方法在我们在使用的时候需要考虑到开头的内容,所以在做匹配的时候并不那么方便,它适合来检测某个字符串是否符合某个正则表达式的规则。
在这里就有另外一个方法search()
,它在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,也就是说,正则表达式可以是字符串的一部分,在匹配时,search()
方法会依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,如果搜索完了还没有找到,那就返回None。
我们把上面的代码中的match()方法修改成search(),这样就得到了匹配结果。所以说,为了匹配方便,我们可以尽量使用search()方法。
2.findall()
在前面我们说了search()
方法的用法,它可以返回匹配正则表达式的第一个内容,但是如果我们想要获取匹配正则表达式的所有内容的话怎么办?这时就需要借助于findall()
方法了。
findall()
方法会搜索整个字符串然后返回匹配正则表达式的所有内容。
3.sub()
正则表达式除了提取信息,我们有时候还需要借助于它来修改文本,比如我们想要把一串文本中的所有数字都去掉,如果我们只用字符串的replace()
方法那就太繁琐了,在这里我们就可以借助于sub()
方法。
示例
将匹配到的数字加1
import re
ret = re.sub(r"\d+", '998', "python = 997")
print(ret)
import re
def add(temp):
strNum = temp.group()
num = int(strNum) + 1
return str(num)
ret = re.sub(r"\d+", add, "python = 997")
print(ret)
ret = re.sub(r"\d+", add, "python = 99")
print(ret)
运行结果
python = 998
python = 100
可以传一个函数进去
4.split()
根据匹配进行切割字符串,并返回一个列表
切割字符串“info:xiaoZhang 33 shandong”
>>>import re
>>>re.split(r":| ","info:xiaoZhang 33 shandong")
['info', 'xiaoZhang', '33', 'shandong']
5.compile()
前面我们所讲的方法都是用来处理字符串的方法,最后再介绍一个compile()方法,这个方法可以讲正则字符串编译成正则表达式对象,以便于在后面的匹配中复用。
例如这里有三个日期,我们想分别将三个日期中的时间去掉,所以在这里我们可以借助于sub()方法,sub()方法的第一个参数是正则表达式,但是这里我们没有必要重复写三个同样的正则表达式,所以可以借助于compile()函数将正则表达式编译成一个正则表达式对象,以便复用。
示例
import re
time1='2017-6-15 11:30'
time2='2017-6-16 12:30'
time3='2017-6-17 11:30'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern,'',time1)
result2 = re.sub(pattern,'',time2)
result3 = re.sub(pattern,'',time3)
print(result1)
print(result2)
print(result3)
运行结果
2017-6-15
2017-6-16
2017-6-17
另外compile()还可以传入修饰符,例如re.S等修饰符,这样在search()、findall()等方法中就不需要额外传了。所以compile()方法可以说是给正则表达式做了一层封装,以便于我们更好地复用。
9. 贪婪和非贪婪
Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;
非贪婪则相反,总是尝试匹配尽可能少的字符。
在"*","?","+","{m,n}"
后面加上?
,使贪婪变成非贪婪。
练习代码
import re
s = "This is a number 234-235-22-423"
r1 = re.match("(.+)(\d+-\d+-\d+-\d+)", s)
print('r1...')
print(r1.groups())
r2 = re.match("(.+?)(.*?)(\d+-\d+-\d+-\d+?)", s)
print('r2...')
print(r2.groups())
r3 = re.match("(.+)(.+?)(\d+-\d+-\d+-\d+?)", s)
print('r3...')
print(r3.groups())
r4 = re.match("(.+)(.*?)(\d+?)(-\d+-\d+-\d+?)", s)
print('r4...')
print(r4.groups())
r5 = re.match("(.+?)(.*)(\d+?)(-\d+-\d+-\d+?)", s)
print('r5...')
print(r5.groups())
分析一波代码
r1(.+)
+ 匹配前一个字符出现1次或者无限次,即最少有一次 从第一个开始匹配,并贪婪的尽可能多的匹配,将23也匹配上,给下一个(\d+)
留一个4,因为(\d+)
至少匹配一个
r2 (.+?)
从第一个开始匹配,并 非贪婪的匹配到一个T
,(.*?)
接着上个表达式继续匹配,匹配到2
的时候发现(\d+-\d+-\d+-\d+?)
符合匹配,就结束匹配得到自己的his is a number
r4 (.+)
贪婪的匹配,发现下一个正则表达式(.*)
可有可无,于是就接着匹配到(\d+?)
然后(.*)
这个组就是空。
运行结果
r1...
('This is a number 23', '4-235-22-423')
r2...
('T', 'his is a number ', '234-235-22-4')
r3...
('This is a number 2', '3', '4-235-22-4')
r4...
('This is a number 23', '', '4', '-235-22-4')
r5...
('T', 'his is a number 23', '4', '-235-22-4')
10. 练习
一、从下面的字符串中取出文本(将标签去掉)
import re
s= '''<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>
<p>5、熟悉 Javascript/CSS/HTML5,JQuery、React、Vue.js</p>
<p> <br></p>
<p>加分项:</p>
<p>大数据,数理统计,机器学习,sklearn,高性能,大并发。</p>
</div>'''
a=re.sub('</?p>|</?div>|<br>| ','',s)
print(a)
运行结果
岗位职责:
完成推荐算法、数据统计、接口、后台等服务器端相关工作
必备要求:
良好的自我驱动力和职业素养,工作积极主动、结果导向
技术要求:
1、一年以上Python开发经验,掌握面向对象分析和设计,了解设计模式
2、掌握HTTP协议,熟悉MVC、MVVM等概念以及相关WEB开发框架
3、掌握关系数据库开发设计,掌握SQL,熟练使用MySQL/PostgreSQL中的一种
4、掌握NoSQL、MQ,熟练使用对应技术解决方案
5、熟悉Javascript/CSS/HTML5,JQuery、React、Vue.js
加分项:
大数据,数理统计,机器学习,sklearn,高性能,大并发。