[python]二十、python正则表达式详解

目录

1、什么是正则?

1.1、正则表达式的优缺点

2、re模块

2.1、match和search

2.1.1、match对象

2.2、findall或者finditer

2.3、sub

2.4、编译正则re.compile("匹配正则")

3、基本正则匹配

3.1、区间匹配

3.2、取反

3.3、或匹配

3.4、占位符

3.5、常用快捷方式

3.6、开始与结束

3.6.1、标志位

4、正则重复

4.1、通配符

4.1.1、"?"

4.1.2、"+"

4.1.3、"*"

4.1.4、{n,m}

4.2、贪婪和非贪婪模式

5、正则分组

5.1、分组向后引用(引用分组)

5.2、findall对非捕获分组和捕获分组的反应

6、零宽断言


1、什么是正则?

正则的目的

  • 数据挖掘
    • 从一大堆文本中找到一小堆文本时。如,从文本是寻找email, ip, telephone等
  • 验证
    • 使用正则确认获得的数据是否是期望值。如,email、用户名、IP地址是否合法等
  • 非必要时慎用正则,如果有更简单的方法匹配,可以不使用正则
  • 指定一个匹配规则,从而识别该规则是否在一个更大的文本字符串中。
  • 正则表达式可以识别匹配规则的文本是否存在
  • 还能将一个规则分解为一个或多个子规则,并展示每个子规则匹配的文本

1.1、正则表达式的优缺点

  • 优点:提高工作效率、节省代码
  • 缺点:复杂,难于理解

2、re模块

re模块是一个标准库,无需安装

官方文档:https://docs.python.org/3/library/re.html

2.1、match和search

  • re.search
    • 查找匹配项
    • 接受一个正则表达式和字符串,并返回发现的第一个匹配。
    • 如果完全没有找到匹配,re.search返回None
  • re.match
    • 从字符串头查找匹配项
    • 接受一个正则表达式和字符串,从主串第一个字符开始匹配,并返回发现的第一个匹配。
    • 如果字符串开始不符合正则表达式,则匹配失败,re.match返回None
>>> import re
>>> result = re.search("sanchuang","hello world,this is sanchuang")   # 前面写匹配字段,后面写要匹配的字符串
>>> result
<_sre.SRE_Match object; span=(20, 29), match='sanchuang'>  # 这是一个match对象
>>> result = re.search("sanchuang1","hello world,this is sanchuang")  # 这里没有匹配到字符串
>>> result
>>> print(result)
None

>>> result = re.search("san.*$","hello world,this is sanchuang")  # 匹配"san"开头的往后所有的字符串
>>> result
<_sre.SRE_Match object; span=(20, 29), match='sanchuang'>

>>> 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'>

# 如果匹配到了就会返回一个match对象;如果没有匹配上那就返回一个None
"""

"""
# match只能从字符串开头查找,开始的部分没有,那就匹配不上。也就是说只匹配字符串的开始

>>> result = re.search("sanchuang","hello world,this is sanchuang  sanchuang") # 这个匹配是一个字符一个字符匹配的
>>> result
<_sre.SRE_Match object; span=(20, 29), match='sanchuang'>
>>> dir(result)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
>>> result.start()
20
>>> result.end()  # 匹配的是一个左闭右开区间
29
>>> result.group()  # 显示匹配的结果
'sanchuang'

2.1.1、match对象

如上代码所示:

  • match.group(default=0):返回匹配的字符串。
    • group是由于正则表达式可以分拆为多个只调出匹配子集的子组。
    • 0是默认参数,表示匹配的整个串,n 表示第n个分组
  • match.start()
    • start方法提供了原始字符串中匹配开始的索引
  • match.end()
    • end方法提供了原始字符串中匹配开始的索引
    • star()和end()组成的区间是左闭右开的区间

2.2、findall或者finditer

作用:找到多个匹配

  • re.findall
    • 查找并返回匹配的字符串,返回一个列表
  • re.finditer
    • 查找并返回匹配的字符串,返回一个迭代器
"""
>>> msg = "i love pythonpython1python2"
>>> re.findall("python",msg)
['python', 'python', 'python']

>>> re.finditer("python",msg)   # finditer出来的是一个迭代器
<callable_iterator object at 0x7f9d520a3470>
>>> for i in re.finditer("python", msg):
...     print(i)
... 
<_sre.SRE_Match object; span=(7, 13), match='python'>
<_sre.SRE_Match object; span=(13, 19), match='python'>
<_sre.SRE_Match object; span=(20, 26), match='python'>

"""

2.3、sub

re.sub('匹配正则' , '替换内容' , 'string') 

  • 将string中匹配的内容替换为新内容
>>> msg = "i love python1 pythonyy python123 pythontt"

# /d,表示[0-9]
>>> re.sub("python\d","**",msg)  # 第一参数表示你要匹配的字符串,第二参数表示你要替换的字符串,第三个参数表示你要进行替换操作的字符串
'i love ** pythonyy **23 pythontt'

2.4、编译正则re.compile("匹配正则")

编译正则的特点:

  • 复杂的正则可复用。
  • 使用编译正则更方便,省略了参数。
  • re模块缓存它即席编译的正则表达式,因此在大多数情况下,使用compile并没有很大 的性能优势
>>> msg = "i love python1 pythonyy python123 pythontt"
>>> reg = re.compile("python[0-9]")
>>> reg.findall(msg)
['python1', 'python1']

3、基本正则匹配

最简单的正则表达式是那些仅包含简单的字母数字字符的表达式,复杂的正则可以实现强大的匹配

3.1、区间匹配

import re
ret = re.findall("[Pp]y","Python3 pth ppython")   # [Py]只占一个字符的位置
print(ret)

ret1 = re.findall("[Pp]yf","Python3 pth ppython")   # 没有匹配上就是一个空列表
print(ret1)
# 按ASCILL编码排序,前面字符的编码一定要比后面字符的编码小
ret2 = re.findall("[0-9a-z]y","Python3 pth ppython")   # [Py]只占一个字符的位置
print(ret2)

#####result
['Py', 'py']
[]
['py']

3.2、取反

ret = re.findall("[^A-z]y","Python3 1yth ppython")
print(ret)

#######result
['1y']

3.3、或匹配

msg = 'xkjsdfjlaskdf lksdjf aslkfjdsfd'
print(re.findall("sk|lk|as", msg))

#####result
['as', 'lk', 'as', 'lk']

3.4、占位符

占位符,表示除了换行符以外的任意一个字符

ret = re.findall("p.thon","Python python pgthon pthon p thon p\nthon")
print(ret)

#######result
['python', 'pgthon', 'p thon']

3.5、常用快捷方式

# \A 匹配字符串的开始
# \b 词边界
# \B 非词边界
# \w 匹配单词字符,数字和中文是可以匹配单词字符
# \W 匹配非单词字符
# \s 匹配空白字符,如:换行符、制表符
# \S 匹配非空白字符
# \d 匹配数字
# \D 匹配非数字
import re
# 正则表达式前面加r:抑制转义
# 原样交给正则表达式引擎去匹配
ret = re.findall(r'\bword', "word abcword 中word #word word123")   #python写好之后交给正则表达式的独立引擎去去执行
# abcword 会被认为是一个单词
# 123word  ,123是单词字符
print(ret)

##### result
['word', 'word', 'word']   # 相应匹配的是word #word word123

3.6、开始与结束

# 匹配开始:^
# 匹配结束:$
msg = """Python
python
python123
python345
"""  # 这里实际上是"Python\npython\npython123\npython345"
ret = re.findall("^[Pp]ython",msg)
print(ret)

#####result
['Python']

3.6.1、标志位

放在findall的第三个参数这里

  • re.M 多行模式
  • re.I  忽略大小写
  • re.S  让 . 表示任意字符(包括换行符)
import re

msg = """Python
python
python123
python345
"""  # 这里实际上是"Python\npython\npython123\npython345"

ret = re.findall("^[Pp]ython",msg,re.M,)
print(ret)

ret = re.findall("^python",msg,re.M|re.I)
print(ret)

ret = re.findall("python.*",msg,re.I)
print(ret)

ret = re.findall("python.*",msg,re.S|re.I)
print(ret)

####### result
['Python', 'python', 'python', 'python']
['Python', 'python', 'python', 'python']
['Python', 'python', 'python123', 'python345']
['Python\npython\npython123\npython345\n']

4、正则重复

4.1、通配符

4.1.1、"?"

# ? 匹配前一项0或者1次
ret = re.findall("py?","py p python pyython")
print(ret)

######result
['py', 'p', 'py', 'py']

4.1.2、"+"

# + 匹配前一项一次以上
ret = re.findall("py+","py p python pyython")
print(ret)

####result
['py', 'py', 'pyy']

4.1.3、"*"

# * 匹配前一项任意次
ret = re.findall("py*","py p python pyython")
print(ret)

#### result
['py', 'p', 'py', 'pyy']

4.1.4、{n,m}

# {n,m} 匹配前一项n到m次,{n,} {,m}
ret = re.findall("py{2,4}","pyyy py pyyyypyypy")
print(ret)


###### result
['pyyy', 'pyyyy', 'pyy']

思考:当我们匹配第一个字段和第三个字段的时候有"pyyy"和"pyyyy",那么为什么不能匹配到"pyy"就退出呢?

那是因为python的默认模式是贪婪模式

4.2、贪婪和非贪婪模式

  • 贪婪模式:匹配尽可能多的字符,贪婪量词为:"*"、"+"、”{n,m}“
  • 非贪婪模式:匹配到就退出。在贪婪量词后面加"?"
import re
msg = "<div>test</div>bb<div>tedst2</div>"
ret = re.findall("<div>.*</div>",msg)  # 贪婪模式
print(ret)
ret = re.findall("<div>.*?</div>",msg)  # 非贪婪模式  + * {n,m}  后面接?都是非贪婪模式
print(ret)

######result
['<div>test</div>bb<div>tedst2</div>']
['<div>test</div>', '<div>tedst2</div>']

5、正则分组

当使用分组时,除了可以获得整个匹配,还能够获得选择每一个单独组,使用 () 进行分组

捕获分组和非捕获分组

  • 捕获分组(正则表达式)

    • 分组匹配上之后会把匹配上的数据放到内存中,并给定一个从1开始的索引

ret = re.search(r"(\d{3})-(\d{3})-(\d{3})","abc123-456-789-aaa")
print(ret.group())
print(ret.group(1))
print(ret.group(2))
print(ret.group(3))

####result
123-456-789
123
456
789
  • 非捕获分组(?:正则表达式)

    • 只分组,不捕获,不会分配内存和下标去保存

ret = re.search(r"(\d{3})-(?:\d{3})-(\d{3})","abc123-456-789-aaa")
print(ret.group())
print(ret.group(1))
print(ret.group(2))

#######result
123-456-789
123
789

5.1、分组向后引用(引用分组)

使用()分用,用\0, \1, \2引用 (\0表示匹配的整个串)

msg = "aa bb aa bb"
print(re.search(r"(\w+)\s(\w+)\s\1\s\2", msg))  # "\1"代表第一个分组

msg = "aa bb aa cc"
print(re.search(r"(\w+)\s(\w+)\s\1\s\2", msg))  # "\1"代表第一个分组

##### result
<re.Match object; span=(0, 11), match='aa bb aa bb'>
None

5.2、findall对非捕获分组和捕获分组的反应

# 使用findall  有捕获分组,只会显示捕获分组的内容
msg = "aa bb aa bb"
print(re.findall(r"(\w+)\s(\w+)\s\1\s\2", msg))  # "\1"代表第一个分组

msg = "comyy@xx.comcom123@qq.comaa@126.combbb@163.comcc@abc.com"
# 把里面123 126 163邮箱捞出来
# aaa@126.com  bbb@163.com
print(re.findall(r"(?:\.com)?(\w+@(?:123|126|163).com)",msg))


#####result
[('aa', 'bb')]
['aa@126.com', 'bbb@163.com']

6、零宽断言

作用:不占用匹配宽度,只用来确定位置

s = "sc1 hello sc2 hello"

# 匹配后面是sc2的hello
print(re.findall(r"\w+ hello(?= sc2)",s))
# 匹配后面不是sc2的hello
print(re.findall(r"\w+ hello(?! sc2)",s))

# 匹配前面是sc2 的hello
print(re.findall(r"(?<=sc2 )hello",s))
# 匹配前面不是sc2 的hello
print(re.findall(r"(?<!sc2 )hello",s))


#####result
['sc1 hello']
['sc2 hello']
['hello']
['hello']
#####截取出下面内容的IP地址
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:16:6a:88 brd ff:ff:ff:ff:ff:ff
    inet 192.168.29.128/24 brd 192.168.29.255 scope global ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe16:6a88/64 scope link 
       valid_lft forever preferred_lft forever
3: ens37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:16:6a:92 brd ff:ff:ff:ff:ff:ff
    inet 192.168.29.71/24 brd 192.168.29.255 scope global ens37
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe16:6a92/64 scope link 
       valid_lft forever preferred_lft forever
"""
print(re.findall(r"(?<=inet ).*(?=/24)",msg)[0])


##### result
192.168.29.128

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FanMY_71

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值