在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个模块来操作,名字为re
import re
(一) 三种基本方法 match,search和findall
在re模块中,通常使用三种方法,match,search和findall,下面对这三种方法进行简单的介绍:
(1).match方法
re.match 尝试从字符串的起始位置匹配一个模式,匹配成功则返回的是一个匹配对象(这个对象包含了我们匹配的信息),如果不是起始位置匹配成功的话,match()返回的是空,
注意:match只能匹配到一个**
下面来看代码理解
s = 'pythonasdfasfdpythonsd6d6'
result = re.match('python', s)
print(result) # <re.Match object; span=(0, 6), match='python'>
print(result.span()) # (0, 6)
print(result.group()) # python
1.通过span()提取匹配到的字符下标
2.通过group()提取匹配到的内容
而s字符串中有3个python的存在,match只能匹配到一个
下面我们改变一下s,得到不一样的结果
s = '1python123python666python888'
result = re.match('python', s)
print(result) #None
因为match从字符串的起始位置开始匹配,这里起始的第一个字符为1,所以匹配失败返回None.
那么我们要如何才能匹配到字符串中间我们想要的部分呢,这个时候就用到了search函数
(2).search方法
re.search 扫描整个字符串,匹配成功则返回的是一个匹配对象(这个对象包含了我们匹配的信息)
注意:search也只能匹配到一个,找到符合规则的就返回,不会一直往后找
同样的,search也只能匹配到一个.
代码如下
s = '1pythonasdfpython8sdf8123sd'
result = re.search('python', s)
print(result) # <re.Match object; span=(1, 7), match='python'>
print(result.span()) # (1, 7)
print(result.group()) # python
当然,若是都找不到则返回None值
s = '1python1d2sd3python66sdgf6python83248dgd'
result = re.search('python98', s)
print(result) # None
*search方法虽然解决了match的从头匹配的弊端,但它也只能匹配到一个,这个时候我们就可以使用findall方法了 *
(3).findall方法:
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表
s = '1python1sd2e3ertpythoertert8'
result = re.findall('python', s)
print(result) # ['python', 'python', 'python']
上面的三种方法看上去只能匹配到简单的字符串,也许我们觉得用一般的方法也可以办到:
比如字符串的index方法(也可以用循序)
print(s.index('python')) # 1
print(s.index('python', 2)) # 10
print(s.index('python', 11)) # 19
那么正则表达式应该用在什么地方呢:
判断用户注册帐号是否满足格式
抓取页面中特定部分数据
判断用户提交的邮箱的格式是否正确 等等等等
那么我们就要了解元字符
(二) 正则表达式对象的常用方法
-
rx.findall(s,start, end):
返回一个列表,如果正则表达式中没有分组,则列表中包含的是所有匹配的内容,
如果正则表达式中有分组,则列表中的每个元素是一个元组,元组中包含子分组中匹配到的内容,但是没有返回整个正则表达式匹配的内容 -
rx.finditer(s, start, end):
返回一个可迭代对象
对可迭代对象进行迭代,每一次返回一个匹配对象,可以调用匹配对象的group()
方法查看指定组匹配到的内容,0表示整个正则表达式匹配到的内容 -
rx.search(s, start, end):
返回一个匹配对象,倘若没匹配到,就返回Nonesearch()
方法只匹配一次就停止,不会继续往后匹配 -
rx.match(s, start, end):
如果正则表达式在字符串的起始处匹配,就返回一个匹配对象,否则返回None -
rx.sub(x, s, m):
返回一个字符串。每一个匹配的地方用x
进行替换,返回替换后的字符串,如果指定m
,则最多替换m
次。对于x
可以使用/i
或者/g<id>id
可以是组名或者编号来引用捕获到的内容。
模块方法re.sub(r, x, s, m)
中的x
可以使用一个函数。此时我们就可以对捕获到的内容推过这个函数进行处理后再替换匹配到的文本。 -
rx.subn(x, s, m):
与re.sub()
方法相同,区别在于返回的是二元组,其中一项是结果字符串,一项是做替换的个数。 -
rx.split(s, m):
分割字符串,返回一个列表,用正则表达式匹配到的内容对字符串进行分割
如果正则表达式中存在分组,则把分组匹配到的内容放在列表中每两个分割的中间作为列表的一部分,如:
rx = re.compile(r"(\d)[a-z]+(\d)")
s = "ab12dk3klj8jk9jks5"
result = rx.split(s)
返回['ab1', '2', '3', 'klj', '8', '9', 'jks5']
rx.flags()
正则表达式编译时设置的标志rx.pattern()
正则表达式编译时使用的字符串
(四)匹配对象的属性与方法
-
m.group(g, ...)
返回编号或者组名匹配到的内容,默认或者0表示整个表达式匹配到的内容,如果指定多个,就返回一个元组 -
m.groupdict(default)
返回一个字典。字典的键是所有命名的组的组名,值为命名组捕获到的内容
如果有default参数,则将其作为那些没有参与匹配的组的默认值。 -
m.groups(default)
返回一个元组。包含所有捕获到内容的子分组,从1开始,如果指定了default值,则这个值作为那些没有捕获到内容的组的值 -
m.lastgroup()
匹配到内容的编号最高的捕获组的名称,如果没有或者没有使用名称则返回None(不常用) -
m.lastindex()
匹配到内容的编号最高的捕获组的编号,如果没有就返回None。 -
m.start(g)
当前匹配对象的子分组是从字符串的那个位置开始匹配的,如果当前组没有参与匹配就返回-1 -
m.end(g)
当前匹配对象的子分组是从字符串的那个位置匹配结束的,如果当前组没有参与匹配就返回-1 -
m.span()
返回一个二元组,内容分别是m.start(g)
和m.end(g)
的返回值 -
m.re()
产生这一匹配对象的正则表达式 -
m.string()
传递给match()
或者search()
用于匹配的字符串 -
m.pos()
搜索的起始位置。即字符串的开头,或者start指定的位置(不常用) -
m.endpos()
搜索的结束位置。即字符串的末尾位置,或者end指定的位置(不常用)
(三) 字符串匹配细节
1. re.match
单字符匹配
以下字符,都匹配单个字符数据。且开头(从字符串0位置开始)没匹配到,即使字符串其他部分包含需要匹配的内容,.match也会返回none
字符 | 功能 | 备注 |
. | 匹配任意一个字符(除了 \n ) | |
[ ] | 匹配 [ ] 中列举的字符 | |
\d | 匹配数字,即 0 ~ 9 | |
\D | 匹配非数字,即不是数字 | |
\s | 匹配空白,即空格,tab键 | |
\S | 匹配非空白 | |
\w | 匹配单词字符,即a-z, A-Z, 0-9, _ (下划线) | |
\W | 匹配非单词字符 |
例如:
. 匹配任意一个字符
使用几个点号就代表几个字符
import re
a = re.match('..','testasdtest')
print(a.group()) #输出te
b = re.match('ab.','testasdtest')
print(b) #返回none,因为表达式是以固定的ab开头然后跟上通配符. 所以必须要先匹配上ab才会往后进行匹配
\d 匹配数字
一个\d代表一个数字。开头没匹配到,即使字符串其他部分包含需要匹配的内容,.match也会返回none
import re
a = re.match('\d\d','23es12testasdtest')
print(a)
b = re.match('\d\d\d','23es12testasdtest')
print(b) #要求匹配三个数字,匹配不到返回none
c = re.match('\d','es12testasdtest')
print(c) #起始位置没有匹配成功,一样返回none
\D 匹配非数字
开头没匹配到,即使字符串其他部分包含需要匹配的内容,.match也会返回none
import re
a = re.match('\D','23es12testasdtest')
print(a) #开头为数字所以返回none
b = re.match('\D\D','*es12testasdtest')
print(b) #返回*e
\s 匹配特殊字符,如空白,空格,tab等
代表数量的元字符
字符 | 功能 | 备注 |
* | 匹配前一个字符出现0次或者无数次,即可有可无 | |
+ | 匹配前一个字符出现1次或者无数次,即至少有1次 | |
? | 匹配前一个字符出现1次或者0次,即要么1次要么没有 | |
{m} | 匹配前一个字符出现m次 | |
{m,} | 匹配前一个字符至少出现m次 | |
{m,n} | 匹配前一个字符出现m到n次 |
匹配边界
字符 | 功能 | 备注 |
^ | 匹配字符串开头 | |
$ | 匹配字符串结尾 | |
\b | 匹配一个单词的边界 | |
\B | 匹配非单词边界 |
匹配分组
字符 | 功能 | 备注 |
| | 匹配左右任意一个表达式 | |
(ab) | 将括号中字符作为一个分组 | |
\num | 引用分组num匹配到的字符串 | |
(?P<name>) | 分组起别名 | |
(?P=name) | 引用别名为name分组匹配到的字符串 |
re.s
findall中另外一个属性re.S
在字符串a中,包含换行符\n,在这种情况下
如果不使用re.S参数,则只在每一行内进行匹配,如果一行没有,就换下一行重新开始。
而使用re.S参数以后,正则表达式会将这个字符串作为一个整体,在整体中进行匹配。
如下要寻找test.*123的数据,因为test和123在不同的行,如果没加re.s的话,他会在每一个进行匹配查找而不是将字符串作为一个整体进行查找
import re
a = """aaatestaa
aaaa123"""
print(re.findall(r'test.*123',a))
print(re.findall(r'test.*123',a,re.S))
sub
查找字符串中所有相匹配的数据进行替换
sub(要替换的数据,替换成什么,要替换的数据所在的数据)
import re
print(re.sub('php','python','php是世界上最好的语言——php'))
#输出 "python是世界上最好的语言——python"
split
对字符串进行分割,并返回一个列表
import re
s = "itcase,java:php-php3;html"
print(re.split(r",",s)) #以,号进行分割
print(re.split(r",|:|-|;",s)) #以,或者:或者-或者;进行分割
print(re.split(r",|:|-|%",s)) #找不到的分隔符就忽略
贪婪与非贪婪
python里的数量词默认是贪婪的,总是尝试尽可能的匹配更多的字符。python中使用?号关闭贪婪模式
如
import re
print(re.match(r"aa\d+","aa2323")) #会尽可能多的去匹配\d
print(re.match(r"aa\d+?","aa2323")) #尽可能少的去匹配\d
“输出:
<re.Match object; span=(0, 6), match='aa2323'>
<re.Match object; span=(0, 3), match='aa2'>
”
import re
s = "this is a number 234-235-22-423"
# 1.贪婪模式
resule = re.match(r"(.+)(\d+-\d+-\d+-\d)",s) #我们本想数字和字母拆解成两个分组
print(resule.groups()) #('this is a number 23', '4-235-22-4')但我们发现输出的结果中23的数字竟然被弄到前面去了
#因为+它会尽可能多的进行匹配,\d,只需要一个4就能满足,所以前面就尽可能多的匹配
# 2.关闭贪婪模式
#在数量词后面加上 ?,进入非贪婪模式,尽可能少的进行匹配
result = re.match(r"(.+?)(\d+-\d+-\d+-\d)",s)
print(result.groups()) #('this is a number ', '234-235-22-4')
“输出:
('this is a number 23', '4-235-22-4')
('this is a number ', '234-235-22-4')
”
&&&&&&&&&&&&&:
key = "威胁"
a = """
<body data-spm="7663354">
<div data-spm="1998410538">
<div class="header">
<div class="container">
<div class="message">
很抱歉,由于您访问的URL有可能对网站造成安全威胁,您的访问被阻断。
<div>您的请求ID是: <strong>
781bad0a16702307419116917e43b3</strong></div>
</div>
</div>
</div>
"""
res = re.search(r'<.*>(.*?%s.*?)<.*?>'%(key),a,re.S)
print(res.group(1).replace("\n","").replace(" ",""))
“结果:
很抱歉,由于您访问的URL有可能对网站造成安全威胁,您的访问被阻断。
”
在html中,“ ”表示空格
其中“&”在html用做转义。
例如:&表示“&”;<表示“<”;>表示“>”等等。
“ (注意此字母必须小写,方可空格)”表示非换行空格;表示文件路径时使用符号“/”分隔,文件名及路径描述可用双引号也可不用引号括起。
2. python中的group方法
group()在正则表达式中用于获取分段截获的字符串,解释如下代码(代码来自网络):
import re
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0) #123abc456,返回整体
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1) #123
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2) #abc
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3) #456
可以看出,正则表达式按照数字-字母-数字的顺序来获取相应字符串,那么分别就是“数字(group(1))--字母(group(2))--数字(group(3))”的对应关系,
其中,group(0)和group()效果相同,均为获取取得的字符串整体。
group和groups是两个不同的函数。
一般,m.group(N) 返回第N组括号匹配的字符。
而m.group() == m.group(0) == 所有匹配的字符,与括号无关,这个是API规定的。
m.groups() 返回所有括号匹配的字符,以tuple格式。
m.groups() == (m.group(0), m.group(1), ...)
group()和groups() 参考:
python中group方法以及与groups的区别_python groups-CSDN博客
python group与groups 详解以及区分_代码group是什么意思-CSDN博客
group与groups:
match和search匹配的返回结果都是对象,如果要获取对应字符串,需要使用group(num) 或 groups() :
group(num=0):
直接调用则返回整个匹配结果,
如果group里面有参数:group(0)代表整个匹配结果,group(1) 列出第一个分组匹配部分,group(2) 列出第二个分组匹配部分,group(3) 列出第三个分组匹配部分,以此类推。
groups()
以元组返回所有分组匹配的字符
附加:
start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;
span([group]) 方法返回 (start(group), end(group))。
没有子组的情况下是返回整个匹配结果的start和end:
只有有group方法的查找方式的结果才有start,end,span,而findall是没有的