学习目的
1.区分正则表达式和Python正则表达式的相同点和不同点
2.使用re模块的相关方法处理数据(重点、难点)
3.区分贪婪模式与非贪婪模式,使用非贪婪模式解决特殊的数据处理问题(难点)
1.概述
正则表达式
正则表达式,英文名为Regular Expression,又称规则表达式。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
在主流操作系统(*nix[Linux, Unix等]、Windows、HP、BeOS等)、主流的开发语言(delphi、Scala、PHP、C#、Java、C++、Objective-c、Swift、VB、Javascript、Ruby以及Python等)、数以亿万计的各种应用软件中,都可以看到正则表达式优美的舞姿。
Python正则表达式
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。同时,re 模块是用c语言写的,其匹配速度非常快。
其中compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。
2.re模块
re.match方法
re.match 尝试从字符串的起始位置匹配一个规则,匹配成功就返回match对象,否则返回None。可以使用group()获取匹配成功的字符串。
语法:re.match(pattern, string, flags=0)
参数说明:
参数 | 描述 |
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式, 如:是否区分大小写,多行匹配等等。 |
示例 (无标志位)
import re
data ="Python is the best language in the world"
# match 只能匹配以xxx开头的子符串,第一个参数是正则,第二个参数是需要匹配的字符串
result=re.match("P",data) #精确匹配,以什么来开头
print(type(result)) #<class 're.Match'>
print(result.group()) ## 匹配成功使用group方法取出字符串
result=re.match("Python",data) #精确匹配,以什么来开头
print(type(result)) #<class 're.Match'>
print(result.group()) ## 匹配成功使用group方法取出字符串
result=re.match("t",data) #精确匹配,t不是什么开头的 所以会报错
print(type(result)) #<class 're.Match'>
print(result.group()) #匹配成功就会返回
输出结果
E:\pythonProject9\venv\Scripts\python.exe E:/pythonProject9/Regex.py
<class 're.Match'>
P
<class 're.Match'>
Python
<class 'NoneType'>
Traceback (most recent call last):
File "E:/pythonProject9/Regex.py", line 14, in <module>
print(result.group()) #匹配成功就会返回
AttributeError: 'NoneType' object has no attribute 'group'
Process finished with exit code 1
示例2(有标志位):
如果同时使用多个标志位使用|分割,比如re.I | re.M
flags可选标志位
修饰符 | 描述 |
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
import re
data ="Python is the best language in the world"
# 如果同时使用多个标志位使用|分割,比如re.I | re.M
res=re.match("p",data,re.I|re.M) #使匹配对大小写不敏感
if res:
print('匹配成功......')
print(res)
print(res.group())
else:
print(res)
print('匹配失败......')
# 如果匹配失败,是没有group函数的 因为一个空对象是None
print(res.group())
输出结果
匹配成功......
<re.Match object; span=(0, 1), match='P'>
P
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。
匹配对象方法 | 描述 |
group(num=0) | 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。 |
groups() | 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。 |
group和groups示例:
import re
str ="Python is the best language in the world"
res=re.match('(.*) the (.*?) .*',str) #is前后的内容
print(res)
print(res.group())
print(res.group(1)) #the前面是Python is
print(res.group(2)) #the后面是best
print(res.groups()) #以元组形式返回两个结果('Python is', 'best')
输出结果
<re.Match object; span=(0, 40), match='Python is the best language in the world'>
Python is the best language in the world
Python is
best
('Python is', 'best')
常用的匹配规则-匹配字符
符号 | 匹配规则 |
.(点) | 匹配任意1个字符除了换行符\n |
[abc ] | 匹配abc中的任意一个字符 |
\d | 匹配一个数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格,tab键 |
\S | 匹配非空白,除空格,tab键之类的 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_ |
\W | 匹配非单词字符 |
示例
import re
str1='aaaa'
#(点.)匹配任意除换行符之外的字符
str1_Test=re.match('.',str1).group() #a
print(str1_Test)
#[ ]匹配中括号中的任意一个字符开头都可以
str2='hello'
str2_Test=re.match('[he]',str2).group() #h
print(str2_Test)
str3='eee'
str3_Test=re.match('[He]',str3).group() #e
print(str3_Test)
#\d匹配一个数字
str='123abc'
str1=re.match('\d',str).group() #1
str2=re.match('\d\d',str).group() #12
print(str1)
print(str2)
#\D匹配非数字,即不是数字
str='aG123asb'
str1=re.match('\D',str).group() #a
str2=re.match('\D\D',str).group() #aG
print(str1)
print(str2)
#\s匹配空白字符串
str=' hello'
str1=re.match('\s',str).group() #a
print("空白字符串"+str1+"空白字符串") #" "
#\S匹配非空白字符串
#str=' hello'
#str1=re.match('\S',str).group() #如果第一个是空格,会报错
str='hello'
str1=re.match('\S',str).group()
print(str1) #h
#\W匹配非单词字符
str=' python'
str1=re.match('\W',str).group() #' '
print(str1)
#\w匹配非单词字符
str='python'
str2=re.match('\w',str).group() #p
print(str2)
输出结果
a
h
e
1
12
a
aG
空白字符串 空白字符串
h
p
Process finished with exit code 0
常用的匹配规则-匹配字符数量
符号 | 匹配规则 |
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
\? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m次 |
{n,m} | 匹配前一个字符出现从n到m次 |
*:匹配前一个字符出现0次或者无限次,即可有可无
import re
ret=re.match('[A-Z][A-Z]*','Mn').group() #M
print(ret)
ret=re.match('[A-Z][a-z]*','Mn').group() #Mn
print(ret)
ret=re.match('[A-Z][a-z]*','Python').group() #Python
print(ret)
输出结果
M
Mn
Python
+ :使用实例,写一个正则匹配python变量名(变量名的语法规则:不能以数字开头,变量名只能包含字母,数字,下划线)
import re
ret=re.match('[a-zA-Z_]+[\w]*','name').group() #name
print(ret)
#ret=re.match('[a-zA-Z_]+[\w]*','123name').group() #name 数字开头不是变量名,不能匹配
name
?:使用实例,匹配0-99的数字
import re
ret=re.match('[0-9]?[0-9]','25').group() #25
print(ret)
ret=re.match('[0-9]?[0-9]','09').group() #09
print(ret)
ret=re.match('[0-9]?[0-9]','9').group() #9
print(ret)
{n,m} :匹配前一个字符出现从n到m次,使用实例,匹配qq邮箱
import re
ret=re.match('[0-9]{6,11}@qq.com','123654@qq.com').group()
print(ret)
#123654@qq.com
注意:大于11位匹配失败,少于6位匹配失败
常用的匹配规则-原生字符串
python字符串中\作为转义字符开头,比如\n表示换行,\t表示tab键,为了表示\本身,再加一个\,成为\\形式 在python中表示路径‘G:\py资料\1-上课资料\4-正则表达式课件\html’
import re
#My_path='C:\Users\Administrator\Desktop\汀澜水岸.xlsx' #会报错
My_path='C:\\Users\\Administrator\\Desktop\\汀澜水岸.xlsx'
print(My_path)
下面使用正则表达式来匹配路径
import re
#My_path=re.match('c:\\D.txt','c:\\D.txt').group() #会报错
My_path=re.match('c:\\\\D.txt','c:\\D.txt').group()
print(My_path)
正则中\也表示转义,所以要匹配字符串中的\需要再次转义
正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\"。
在python中有原生字符串,在字符串前面加上r表示字符串中的\不转义
import re
My_path=re.match(r'c:\\D.txt','c:\\D.txt').group()
print(My_path)
常用的匹配规则-匹配开头结尾
符号 | 匹配规则 |
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
示例:匹配163.com的邮箱地址
import re
ret = re.match('[\w]{4,20}@163\.com','python@163.com').group()
print(ret) #python@163.com
ret = re.match('[\w]{4,20}@163\.com','python@163.comsdsdds').group()
print(ret) #python@163.com 输出不完整
# 通过$来限定结尾
ret = re.match('[\w]{4,20}@163\.com$','python@163.comsdsdds').group()
print(ret) #报错,不是以com结尾的
输出结果
python@163.com
python@163.com
Traceback (most recent call last):
File "E:/pythonProject9/Regex.py", line 127, in <module>
ret = re.match('[\w]{4,20}@163\.com$','python@163.wercom').group()
AttributeError: 'NoneType' object has no attribute 'group'
常用的匹配规则- 分组匹配
符号 | 匹配规则 |
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P) | 分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
|:匹配出0-100之间的数字
import re
ret= re.match('[1-9]?\d','78').group()
print(ret)
ret= re.match('[1-9]?\d$|100','78').group()
print(ret)
ret= re.match('[1-9]?\d$|100','100').group()
print(ret)
输出结果
78
78
100
(ab) 分组:分别匹配电号码区号和电话号码
import re
ret= re.match('([^-]*)-(\d*)','0756-12345678').group()
print(ret)
ret= re.match('([^-]*)-(\d*)','0756-12345678').group(0)
print(ret)
ret= re.match('([^-]*)-(\d*)','0756-12345678').group(1)
print(ret)
ret= re.match('([^-]*)-(\d*)','0756-12345678').group(2)
print(ret)
输出结果
0756-12345678
0756-12345678
0756
12345678
\ :匹配html中的文本 \2 \1这种是直接引用之前的定义格式
import re
ret=re.match('<[a-zA-Z]*>(.*)</[a-zA-Z]*>','<div>hello world</div>').group()
print(ret)
ret=re.match('<[a-zA-Z]*>(.*)</[a-zA-Z]*>','<div>hello world</div>').group(1)
print(ret)
ret=re.match(r'<([a-zA-Z]*)>.*</\1>','<div>hello world</div>').group()
print(ret) # \1 表示引用前面第一组
'''
在使用组引用的时候如果有多个组就容易造成混乱,比如下面多个组引用,
这样很混乱也不容易阅读
'''
ret = re.match(r'<(\w*)><(\w*)>.*</\2></\1>','<div><h1>www.baidu.com</h1></div>').group()
print(ret)
'''
解决上面这个问题得给分组起别名
(?P)分组起别名 P必须是大写
(?P=name)引用别名为name分组匹配到的字符串
'''
ret = re.match(r'<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>',
'<div><h1>www.baidu.com</h1></div>').group()
print(ret)
输出结果
<div>hello world</div>
hello world
<div>hello world</div>
其实我们发现html的标签都是成对出现的,这样匹配规则html就可以引用组来实现,避免重复写。
\1 表示引用前面第一组
在使用组引用的时候如果有多个组就容易造成混乱,比如上面多个组引用,这样很混乱也不容易阅读
解决上面这个问题得给分组起别名
(?P)分组起别名
(?P=name)引用别名为name分组匹配到的字符串
re.complie方法
complie将正则表达式模式编译成一个正则表达式对象
reg = re.compile(pattern) result = reg.match(string)
等效于 result = re.match(pattern, string)
使用re.compile()和保存所产生的正则表达式对象重用效率更高
示例
import re
ret_obj=re.compile('\d{4}')
#开始去使用模式对象ret_obj
ret=ret_obj.match('123456')
print(ret.group())
#等同于
ret=re.match('\d{4}','123456').group()
print(ret)
import re
data='华为是华人的骄傲华侨人'
retobj=re.compile('华.') #创建一次 多次使用
print(retobj.search(data))
print(retobj.findall(data))
re.search方法
search 在全文中匹配一次,匹配到就返回
语法:re.search(pattern, string, flags=0)
函数参数说明:
参数 | 描述 |
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
flags | 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。 |
示例:扫描字符串,寻找的第一个由该正则表达式模式产生匹配的位置,并返回相应的MatchObject实例
import re
data='我爱我伟大的祖国,I love China,China is a great country'
re=re.search('China',data) #只返回第一个对象,找到就返回
print(re)
print(re.group())
re.findall方法
findall 匹配所有返回一个列表,这个方法使用频率较高
语法:findall(string[, pos[, endpos]])
参数说明:
参数 | 描述 |
string | 待匹配的字符串。 |
pos | 可选参数,指定字符串的起始位置,默认为 0。 |
endpos | 可选参数,指定字符串的结束位置,默认为字符串的长度。 |
查询字符串中某个正则表达式全部的非重复出现的情况 返回的是一个列表
import re
data='华为是华人的骄傲华侨人'
re=re.findall('华.',data)
print(re)
运行结果
['华为', '华人', '华侨']
示例:使用findall来匹配出段子网中的所有段子
re.sub方法
sub 将匹配到的数据进行替换
语法:sub(pattern, repl, string, count=0, flags=0)
参数说明:
参数 | 描述 |
pattern | 正则中的模式字符串。 |
repl | 替换的字符串,也可为一个函数。 |
string | 要被查找替换的原始字符串。 |
count | 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。 |
flags | 标志位,用于控制正则表达式的匹配方式 |
import re
data="Python是很受欢迎的编程语言,我喜欢Python"
pattern='[a-zA-Z]+' #字符集的范围 +号代表
res=re.sub(pattern,'C#',data)
print(res)
#subn 实现目标的搜索和替换,还返回被替换的数量
res=re.subn(pattern,'C#',data)
print(res)
运行结果
C#是很受欢迎的编程语言,我喜欢C#
('C#是很受欢迎的编程语言,我喜欢C#', 2)
re.split方法
split 根据匹配进行切割字符串,并返回一个列表
语法:split(pattern, string, maxsplit=0, flags=0)
参数说明:
参数 | 描述 |
pattern | 匹配的正则表达式 |
string | 要匹配的字符串。 |
maxsplit | 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。 |
flags | 标志位,用于控制正则表达式的匹配方式 |
import re
data='百度,腾讯,阿里,华为,360'
re=re.split(",",data)
print(type(re))
print(re)
运行结果
<class 'list'>
['百度', '腾讯', '阿里', '华为', '360']
3.贪婪模式与非贪婪模式
概述
Python里数量词默认是贪婪的,总是尝试匹配尽可能多的字符,非贪婪则相反,总是尝试匹配尽可能少的字符。
在"*","?","+","{m,n}"后面加上?,使贪婪变成非贪婪.
import re
#贪婪模式,取最多的
ret=re.match('\d{6,9}','111222333')
print(ret.group())
#非贪婪模式,取最少的
ret=re.match('\d{6,9}?','111222333') #加一个?就是非贪婪模式
print(ret.group())
content='aacbscbc'
#贪婪模式,取最少的
pattern=re.compile('a.*b')
result=pattern.search(content)
print('贪婪模式%s'%result.group())
#贪婪模式,取最多的
pattern=re.compile('a.*?b') #加一个?就是非贪婪模式
result=pattern.search(content)
print('非贪婪模式%s'%result.group())
运行结果
111222333
111222
贪婪模式aacbscb
非贪婪模式aacb
小结
元字符 | 说明 |
\d | 匹配任意单个数字 |
\D | 匹配非数字 |
\w | 匹配任意单个字母数字下划线a-zA-Z_ |
\W | 匹配\w之外的任意单个字符 |
\s | 匹配单个空格 |
\S | 匹配非空白,除空格,tab键之类 |
\n | 匹配换行符 |
. | 匹配任意单个字符【换行符除外】 |
\num | 匹配前面引用的分组 |
正则表达式 | 说明 | 正确 | 错误 |
A{3} | 精准N次匹配 | AAA | AA、A、AAAA |
A{3,} | 最少出现n次 | AAA | A |
\d{3,5} | 约定出现最少次数和最大次数 | ||
\d* | 可以出现0次至无限次相当于{0,} | ||
\d+ | 最少出现一次,相当于{1,} | ||
\d? | 最多出现一次,相当于{0,1} | 1 | 12 |
定位匹配 | |||
正则表达式 | 说明 | 正确 | 错误 |
^A.* | 开头匹配 | ABC | CBA |
.*A$ | 尾部匹配 | CBA | ABC |
^A.*A$ | 全字匹配 | ACCA | BAAA |
字符的范围匹配 | |||
正则表达式 | 说明 | 正确 | 错误 |
A | 精准匹配 | A | a |
x|y | 允许匹配左右两边的字符 | x 或者 y | n |
[xyz] | 字符集合允许出现集合内任意单个字符 | x y z | a b c |
[a-z][A-Z][0-9] | 字符范围 | ||
[^xyz][^0-9] | 取反,集合内字符不允许出现 |