文章目录
正则表达式
正则的目的
- 数据挖掘
从一大堆文本中找到一小堆文本时。如,从文本是寻找email, ip,telephone等 - 验证
使用正则确认获得的数据是否是期望值。如,email、用户名是否合法等 - 非必要时慎用正则,如果有更简单的方法匹配,可以不使用正则
- 指定一个匹配规则,从而识别该规则是否在一个更大的文本字符串中。
- 正则表达式可以识别匹配规则的文本是否存在
- 还能将一个规则分解为一个或多个子规则,并展示每个子规则匹配的文本
正则表达式的优缺点
- 优点:提高工作效率、节省代码
- 缺点:复杂,难于理解
什么时候用正则表达式
- 在一大推文本字符串中找到自己想要的字符串
- 过滤 爬虫
- 验证输入是否合法
re模块(标准库)函数
查找一个匹配项
- search:查找任意位置 的匹配项
- match:必须从字符串开头匹配
- fullmatch:整个字符串与正则完全匹配
re.search
>>> import re
>>> result = re.search("sanchuang","hello world,this is sanchuang")
>>> result
<_sre.SRE_Match object; span=(20, 29), match='sanchuang'>
>>> result = re.search("sanchuang123","hello world,this is sanchuang")
>>> result
>>> print(result)
None
re.match
- 从字符串头查找匹配项
- 接受一个正则表达式和字符串,从主串第一个字符开始匹配,并返回发现的第一个匹配。
- 如果字符串开始不符合正则表达式,则匹配失败,re.match返回None
- r’ ‘ 中的r代表的是raw(原始字符串)
- 原始字符串与正常字符串的区别是原始字符串不会将\字符解释成一个转义字符
- 正则表达式使用原始字符很常见且有用
match.group(default=0):返回匹配的字符串。
- group是由于正则表达式可以分拆为多个只调出匹配子集的子组。
- 0是默认参数,表示匹配的整个串,n 表示第n个分
match.groups()
groups返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
match.start()
start方法提供了原始字符串中匹配开始的索引
match.end()
end方法提供了原始字符串中匹配开始的索引
match只能从字符串开头查找,开始的部分没有,那就匹配不上
>>> result = re.match("san.*$","hello world,this is sanchuang")
>>> result
>>> result = re.match("hello","hello world,this is sanchuang")
>>> result
<_sre.SRE_Match object; span=(0, 5), match='hello'>
result.start()
0 #开始的位置
result.end()
5 #结束的位置
result.group()
sanchuang #匹配的结果
如果匹配到了,就会返回match对象,没有匹配返回None
re.fullmatch
>>> result = re.match("hello","hello world,this is sanchuang")
>>> result
None
>>> result = re.match("hello","hello")
>>> result.group()
hello
查找多个匹配项
- fubdall:从字符串任意位置查找,返回一个列表
- finditer:从字符串任意位置查找,返回一个迭代器
findall 和 finditer
>>> msg = "i love pythonpython1 python2"
>>> re.findall("python",msg)
['python', 'python', 'python']
>>> re.finditer("python",msg)
<callable_iterator object at 0x7fc3e6126470>
>>> for i in re.finditer("python",msg):
... print(i,i.group())
...
<_sre.SRE_Match object; span=(7, 13), match='python'> python
<_sre.SRE_Match object; span=(13, 19), match='python'> python
<_sre.SRE_Match object; span=(21, 27), match='python'> python
替换
re.sub
将string中匹配的内容替换为新内容
>>> msg = "i love python1 pythonyy python123 pythontt"
>>> re.sub("python[0-9]","**",msg)
'i love ** pythonyy **23 pythontt'
>>> msg = "i love python1 pythonyy python123 pythontt"
>>> re.sub("python\b","**",msg)
>>> msg = "i love python1 pythonyy python123 pythontt"
>>> re.sub("python\d","**",msg)
'i love ** pythonyy **23 pythontt'
正则表达式重用,先把正则表达式编译成一个对象
正则编译
re.compile
编译正则的特点:
- 复杂的正则可复用。
- 使用编译正则更方便,省略了参数。
- re模块缓存它即席编译的正则表达式,因此在大多数情况下,使用compile并没有很大 的性能优势
>>> reg = re.compile("python\d")
>>> reg.findall(msg)
['python1', 'python1']
基本正则匹配
最简单的正则表达式是那些仅包含简单的字母数字字符的表达式,复杂的正则可以实现强大的匹配
区间:[]
正则匹配区分大小写
匹配a或b:a|b
匹配cat或dog
取反:[^abc]
匹配a+非小写字母
任意字符:“.”占位符
匹配任何(除\n外)的单个字符,它仅仅只以出现在方括号字符组以外
#区间匹配
import re
ret = re.findall("[Pp]y","Python3 pth pp ppython") #[]中任选一个字符匹配
print(ret)
#按编码排序
ret = re.findall("[A-z]y","python3 pth paaapaqzython")
print('aaaa',ret)
ret = re.findall("[^A-z]y","Python3 1ypth ppython")
print(ret)
#或匹配
msg = "xlo soaodjas xsak dsa xx"
print(re.findall("so|lo",msg))
# . 占位符 表示除了换行符以外的任意一个字符
ret = re.findall("p.thon","Python python pgthon pthon p thon p\nthon")
print(ret)
输出:
['Py', 'py']
aaaa ['py', 'zy']
['1y']
['lo', 'so']
['python', 'pgthon', 'p thon']
基本正则匹配 - 快捷方式
- \A : 匹配字符串的开始
- \b :词边界(123数字不算词边界)
- \B : 非词边界
- \w : 匹配单词字符
- \W : 匹配非单词字符
- \s : 匹配空白字符(包含,换行,空格)
- \S : 匹配非空白字符
- \d : 匹配数字
- \D : 匹配非数字
基本正则匹配 - 开始与结束
正则表达式字符前面加r,让转义字符原样交给正则表达式引擎去匹配
如果正则表达式有反斜杠,建议前面加r
import re
ret = re.findall(r'\bword',"啊word abcword 1word #word word123 $word")
ret1 = re.findall(r'\wword',"啊word abcword 1word #word word123 $word")
print(ret,ret1)
输出:
['word', 'word', 'word'] ['啊word', 'cword', '1word']
开始与结束
- 匹配开始:^
- 匹配结束:$
msg = """Python
python
python123
python345
"""
ret = re.findall("^[Pp]ython",msg)
print(ret)
正则重复 - 通配符
- ? 匹配前一项0-1次
+
匹配前一项n-1次
*
匹配前一项0-n次,任意次
print("---------通配符---------")
ret = re.findall("py?","py p python pyython")
print(ret)
ret = re.findall("py+","py p python pyython")
print(ret)
ret = re.findall("py*","py p python pyython")
print(ret)
#{n,m} 匹配前一项n-m次{n,} {,m}
ret = re.findall("py{2,4}","pyyy py pyyyyypyypy")
print(ret)
正则标记
- re.M多行匹配
- re.I 忽略大小写
- re.S 让.表示任意字符(包括换行符)
ret = re.findall("Python",msg,re.M|re.I)
print(ret)
ret = re.findall("[Pp]ython",msg,re.M)
print(ret)
ret = re.findall("Python.*",msg,re.I)
print(ret)
ret = re.findall("Python.*",msg,re.I|re.S)
print(ret)
贪婪与非贪婪模式
贪婪模式与非贪婪模式
- 贪婪模式(.*): 匹配尽可能多的字符-默认模式
- 非贪婪模式(.?): 正则 \d*?
- 匹配出
test1,test2
#默认贪婪模式 -- 尽可能匹配长的
msg = "<div>test</div>bb<div>tedst2</div>"
ret = re.findall("<div>.*</div>",msg)
print(ret)
ret = re.findall("<div>.*?</div>",msg)
print(ret)
正则分组
当使用分组时,除了可以获得整个匹配,还能够获得选择每一个单独组,使用 () 进行分组
ret = re.search(r"(\d{3})-(\d{3})-(\d{3})","abc123-456-789aaa")
print(ret.group())
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))
捕获分组和非捕获分组
#捕获分组(正则表达式)(以上)
# 分组匹配上之后,会把匹配上的数据放到内存中,并给定一个从1开始的索引
#非捕获分组(?:正则表达式)
# 只分组不捕获
ret = re.search(r"(\d{3})-(?:\d{3})-(\d{3})","abc123-456-789aaa")
print(ret.group())
print(ret.group(1))
print(ret.group(2))
引用分组
使用()分用,用\0, \1, \2引用 (\0表示匹配的整个
#使用findall 有捕获分组的话,只会显示捕获分组的内容
msg = "aa bb aa bb"
print(re.search(r"(\w+)\s(\w+)\s\1\s\2",msg))
print(re.findall(r"(\w+)\s(\w+)\s\1\s\2",msg))
msg = "aa bb aa cc"
print(re.search(r"(\w+)\s(\w+)\s\1\s\2",msg))
msg = "comyy@123.comcom123@qq.comaaa@126.combbb@163.comcc@abc.com"
#把里面123 126 163 邮箱捞出来
print(re.findall(r"(?:\.com)?(\w+@(?:123|126|163).com)",msg))
正则断言
正则表达式的断言分为:先行断言(lookahead)和后行断言(lookbehind)
4种形式:
- (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
- (?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
- (?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
- (?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertio
零宽
- ^python
正向、负向
- 正向:匹配(?=pattern)/(?<=pattern)
- 负向:不匹配(?!pattern)/(?<!pattern)
先行、后行
- 先行:向后匹配(?=pattern)/(?!pattern)
- 后行:向前匹配(?<=pattern)/(?<!pattern)
零宽正向先行断言:(?=pattern)
- 代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern。
- 匹配字符串re,后面能接gular(但不匹配宽度
#确定位置,不占用匹配宽度
s = "sc1 hello sc2 hello"
#匹配后面是 sc2的hello
print(re.findall(r"\w+ hello(?= sc)",s))
零宽负向先行断言:(?!pattern)
- 代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配pattern。
- 匹配字符串re,但是后面不能接gular
#匹配后面不是 sc2的hello
print(re.findall(r"\w+ hello(?! sc)",s))
零宽正向后行断言:(?<=pattern)
- 代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配pattern。
- 匹配gular,但前面必须是re
#匹配前面是sc2 的hello
print(re.findall(r"(?<=sc2 )hello",s))
零宽负向后行断言 :(?<!pattern)
- 代表字符串中的一个位置,紧接该位置之前的字符序列不能匹配pattern。
- 匹配gular,但前面不能是re
#匹配前面不是sc2 的hello
print(re.findall(r"(?<!sc2 )hello",s))
msg = """
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:9e:70:2c brd ff:ff:ff:ff:ff:ff
inet 192.168.174.130/24 brd 192.168.174.255 scope global noprefixroute ens33
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe9e:702c/64 scope link
valid_lft forever preferred_lft forever
"""
print(re.findall(r"(?<=inet ).*(?=/24)",msg))
输出:['192.168.174.130']