python — 正则表达式

正则表达式

正则的目的

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

什么时候用正则表达式

  1. 在一大推文本字符串中找到自己想要的字符串
    1. 过滤 爬虫
  2. 验证输入是否合法

re模块(标准库)函数

查找一个匹配项

  1. search:查找任意位置 的匹配项
  2. match:必须从字符串开头匹配
  3. 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

查找多个匹配项

  1. fubdall:从字符串任意位置查找,返回一个列表
  2. 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']
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值