前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >万字长文详解Python正则表达式及re模块

万字长文详解Python正则表达式及re模块

作者头像
龙哥
发布2020-06-12 14:46:41
2.4K0
发布2020-06-12 14:46:41
举报
文章被收录于专栏:Python绿色通道Python绿色通道

大家好,偷学Python系列是由小甜同学从初学者的角度学习Python的笔记,其特点就是全文大多由新手易理解代码与注释及动态演示。刚入门的读者千万不要错过!

本文带来的是偷学Python第三十一天:Python正则表达式语法及re模块的使用,其他内容将在近期更新完毕,本文主要将涉及以下内容

正则表达式基础

什么是正则表达式

不管是使用Windows的搜索工具,还是在word文档里面的查找和替换,肯定都用过*?这种通配符,如果想要在某个目录下找到自己写的笔记,小甜最常用的方法就是*.md就可以找到当前文件夹下所有的Markdown文件了;

正则表达式可以理解为超复杂的通配符,可以比通配符匹配的更为精准,正规一点的说法就是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。比如密码的限制(8位以上16位以内,不能出现特殊字符等等)、邮箱的格式,距离生活近一点的就是某手上的"你个萌萌",王者荣耀上的"你个**"。这些都是正则表达式的运用。

字符类

但是如果想查到一组限定的字符,比较原音字母(a, e, i, o, u),特殊字符(*, &, %, @)这怎么弄呢?

在正则表达式中使用[]将一组字符包裹起来,就到达了自己的需要,例如原音字符[aeiou],特殊字符[*&%@]即可完美的匹配任意一个!

元字符

如果想要匹配.?*这些符号,可以使用转义字符\,比如\.,其他元字符如下

正则中的重复

语法

说明

{n,m}

重复n到m次

{n,}

重复n或更多次

{n}

重复n次

?

重复0或1次,相当于{0,1}

+

重复1次或更多次,相当于{1,}

*

重复0次或更多次,相当于{0,}

分支条件

|把不同的规则分隔开,从左到右地测试每个条件,如果满足了某个分支的话,就不会去再管其它的条件了(就是如果有一个表达式为true,就不会执行后面的表达式,如果一直为false就一直执行)

来一个小栗子:中国地区的手机号样式一般如下

代码语言:javascript
复制
0317-1234567
010-1234567
0317-12345678
010-12345678
(010)1234567
0317 12345678

上面基本罗列出来了所有的手机号样式,现在假设有一个文本框,里面只能输入这种手机号,如果不符合格式,就会给出提示。正则表达式可以这样写?

代码语言:javascript
复制
^\(0\d{2,3}\)\d{7,8}$|^0\d{2,3}[-\s]?\d{7,8}$

首先这是一个有分支条件的式子,第一个式子依次是表示字符串的开始和结尾^ ,然后是'\('转义(,0,数字出现2到3次转义)数字出现7到8次。第二个式子依次是表示字符串的开始和结尾^ ,然后是0,数字出现2到3次[-\s]{1}是-符号或空格符号出现出现1次,数字出现7到8次。这样再配合编程语法就可以完成这样一个限制输入的内容!

贪婪和懒惰

贪婪(尽可能多)

a.*f(abcdefbcedf) → abcdefbcedf

懒惰(尽可能少)

a.*?f(abcdefbcedf) → abcdef

语法

说明

??

重复0或1次,但尽可能少重复

+?

重复1次或更多次,但尽可能少重复

*?

重复0次或更多次,但尽可能少重复

{n,m}?

重复n到m次,但尽可能少重复

{n,}?

重复n或更多次,但尽可能少重复

{n}?

重复n次,但尽可能少重复

分组

在正则表达式中使用()进行分组,分组之后就可以对某个分组进行操作,比如重复某个分组,对引用某个分组,对某个分组进行命名,语法格式如下:

代码语言:javascript
复制
(?P<name>...)

使用小括号进行分组,?P<name>进行命名,如果想在后面进行引用的话使用(?P=name)来进行引用,示例如下

代码语言:javascript
复制
(\d{1,3}\.){3}\d{1,3}

这样就可以完成一个简单的匹配IP地址的表达式!

零宽断言

零宽断言就跟它的名字一样,是一种零宽度的匹配,它匹配到的内容不会保存到匹配结果中去,最终匹配结果只是一个位置而已。作用是给指定位置添加一个限定条件,用来规定此位置之前或者之后的字符必须满足限定条件才能使正则中的字表达式匹配成功。

零宽度正预测先行断言

(?=exp) 零宽度正预测先行断言,自身出现的位置的后面能匹配表达式exp,例如想要匹配以ing结尾的单词显示又不需要ing,这个时候就需要零宽度正预测先行断言;这么说比较抽象,直接上栗子

代码语言:javascript
复制
eating looking singing writing shopping

不匹配每个单词的ing的正则表达式?

代码语言:javascript
复制
\w+(?=ing)

零宽度正回顾后发断言

(?<=exp)零宽度正回顾后发断言,自身出现的位置的前面能匹配表达式exp,就是匹配前面的是某个字符且不匹配他,举个栗子

匹配re开头的单词不匹配re,此处用到的软件为RegexBuddy

负向零宽断言

零宽度负预测先行断言

(?!exp)零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp,简单点说就是后面不能出现某个东东,我们来查找包含Windows的字符串,但是Windows后面不能是10

这里仅仅查找出来了3个!

零宽度负回顾后发断言

(?<!exp) 零宽度负回顾后发断言,来断言此位置的前面不能匹配表达式exp,Python中提供的re模块使Python拥有全部正则表达式的功能!

re模块

正则表达式的修饰符

查找单个匹配项:map

re.match 如果 string 开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的匹配对象 。如果没有匹配,就返回 None ;注意它跟零长度匹配是不同的。语法格式如下:

代码语言:javascript
复制
re.match(pattern, string, flags=0)

pattern是匹配的正则表达式,string是要匹配的字符串。flags是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

匹配成功re.match方法返回一个匹配的对象,否则返回None,示例代码如下

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string1 = "hello python"
string2 = "hell5o python"
pattern = r"[a-z]+\s\w+"  # a-z出现1次到任意次加一个\s加任意字符出现1次到任意次
print(re.match(pattern, string1))  # <re.Match object; span=(0, 12), match='hello python'>
print(re.match(pattern, string2))  # None

开局导入re模块,r""表示为一个正则表达式,因为string2中间出现了一个数字5 所以不匹配!

查找单个匹配项:group

re.group是从Match对象中获取结果的,不过不分组默认为0,分组索引则从0开始(0是完整的一个匹配),如果多个分组,则第一个分组是1;也可以为其命名使用,示例代码如下

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string1 = "hello python"
string2 = "hell5o python"
pattern = r"[a-z]+\s\w+"
pattern1 = r"(\w+)(\s)(\w+)"
pattern2 = r"(?P<first>\w+\s)(?P<last>\w+)"  # 命名分组
print(re.match(pattern, string1))  # <re.Match object; span=(0, 12), match='hello python'>
print(re.match(pattern, string1).group())  # hello python
print(re.match(pattern, string2))  # None
print(re.match(pattern1, string2).group(0))  # hell5o python
print(re.match(pattern1, string2).group(1))  # hell5o
print(re.match(pattern1, string2).group(2))  # 这里匹配的是那个空格
print(re.match(pattern1, string2).group(3))  # python
print(re.match(pattern2, string2).group("last"))  # python

查找单个匹配项:search

re.search 扫描整个字符串找到匹配样式的第一个位置,并返回一个相应的匹配对象 。如果没有匹配,就返回一个 None ;注意这和找到一个零长度匹配是不同的。语法结构和match是一样的,示例代码如下

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string = "Hi World Hello python"
pattern = r"Hello python"
print(re.search(pattern, string).group())  # Hello python
print(re.match(pattern, string))  # None

两者的区别为:re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None,而re.search匹配整个字符串,直到找到一个匹配。

查找单个匹配项:fullmatch

re.fullmatch如果整个 string 匹配这个正则表达式,就返回一个相应的匹配对象 。否则就返回 None ;注意跟零长度匹配是不同的。语法格式跟上面的也是一样的,示例代码如下

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string = "Hi World Hello python"
pattern = r"Hi World Hello python"
pattern1 = r"hi World hello python"

print(re.fullmatch(pattern, string))  # <re.Match object; span=(0, 21), match='Hi World Hello python'>
print(re.fullmatch(pattern1, string))  # None

三者的区别

  • match:字符串开头匹配
  • search:查找任意位置的匹配项
  • fullmatch:整个字符串要与正则表达式完全匹配

匹配对象

匹配对象总是有一个布尔值 True。如果没有匹配的话match()search() 返回 None 所以可以简单的用 if 语句来判断是否匹配,示例代码

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string = "Hi World Hello python"
pattern = r"Hello python"
match1 = re.search(pattern, string)
match2 = re.match(pattern, string)
if match1:
    print(match1.group())  # Hello python

if match2:  # 因为match2的值为none所以不执行
    print(match2.group())

查找多个匹配对象——compile

re.compile将正则表达式的样式编译为一个正则对象,可以用于匹配,语法结构?

代码语言:javascript
复制
re.compile(pattern, flags=0)

pattern是匹配的正则表达式,flags是标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

查找多个匹配对象——findall

re.findall在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。与match 和 search 不同的是 match 和 search 是匹配一次 findall 匹配所有。语法结构为

代码语言:javascript
复制
re.findall(string[, pos[, endpos]])
  • string 待匹配的字符串。
  • pos 可选参数,指定字符串的起始位置,默认为 0。
  • endpos 可选参数,指定字符串的结束位置,默认为字符串的长度

查找多个匹配对象——finditer

pattern 在 string 里所有的非重复匹配,返回为一个迭代器保存了匹配对象 。string 从左到右扫描,匹配按顺序排列。空匹配也包含在结果里。语法结构同match,示例代码

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
from collections.abc import Iterator  # 导入判断是否为迭代器的对象
string = "hello python hi javascript"
pattern = r"\b\w+\b"
pattern_object = re.compile(r"\b\w+\b")
print(type(pattern_object))  # <class 're.Pattern'>

findall = pattern_object.findall(string)
for i in findall:
    print(i)

finditer = re.finditer(pattern, string)
# 判断是否为迭代器
print(isinstance(finditer, Iterator))  # True
for _ in range(4):
    finditer1 = finditer.__next__()  # 取出下一个值
    print(finditer1.group())
'''
--循环结果--
hello
python
hi
javascript
'''

如果有超大量的匹配项的话,返回finditer的性能要优于findall,这就是列表和迭代器的区别,在第二十一天的Python中的生成式和生成器会提到!

分割——split

re.split方法按照能够匹配的子串将字符串分割后返回列表,语法结构

代码语言:javascript
复制
re.split(pattern, string[, maxsplit=0, flags=0])
  • pattern:分隔的正则表达式
  • string:分隔的文本
  • maxsplit:分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
  • flags:标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

示例代码

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string = '''hello hi    good morning
goodnight
python
javascript
Linux
'''
pattern = r'\s+'  # 以空格回车制表符为分隔符
print(re.split(pattern, string))  # 不限制次数分隔
# ['hello', 'hi', 'good', 'morning', 'goodnight', 'python', 'javascript', 'Linux', '']
print(re.split(pattern, string, 5))  # 分隔5次
# ['hello', 'hi', 'good', 'morning', 'goodnight', 'python\njavascript\nLinux\n']

str模块的split不同的是,re模块的split支持正则

替换——sub

re.sub用于替换字符串中的匹配项,语法结构如下

代码语言:javascript
复制
re.sub(pattern, repl, string, count=0, flags=0)
  • pattern : 正则中的模式字符串。
  • repl : 替换的字符串,也可为一个函数。
  • string : 要被查找替换的原始字符串。
  • count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。
  • flags : 编译时用的匹配模式,数字形式。

到这里就可以完成一个某手的评论区,修改不良评论的小案例

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
string = input("请输入评论:")
pattern = r"[美丽可爱大方]{1}"  # 检测的字符
print(re.sub(pattern, "萌", string))

替换——subn

行为与 sub() 相同,但是返回一个元组 (字符串, 替换次数).

escape

re.escape(pattern)转义 pattern 中的特殊字符。例如正则里面的元字符,示例代码如下

代码语言:javascript
复制
"""
-*- coding:uft-8 -*-
author: 小甜
time:2020/5/30
"""
import re
pattern = r'\w\s*\d\d.'
# 打印pattern的特殊字符
print(re.escape(pattern))  # \\w\\s\*\\d\\d\.

注:任意可能包含正则表达式元字符的文本字符串进行匹配,它就是有用的,不过容易出现错误,手动转义比较好!

purge

re.purge()用于清除正则表达式的缓存。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python绿色通道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正则表达式基础
  • 什么是正则表达式
  • 字符类
  • 元字符
  • 正则中的重复
  • 分支条件
  • 贪婪和懒惰
  • 分组
  • 零宽断言
  • 零宽度正预测先行断言
  • 零宽度正回顾后发断言
  • 负向零宽断言
  • 零宽度负预测先行断言
  • 零宽度负回顾后发断言
  • re模块
  • 正则表达式的修饰符
  • 查找单个匹配项:map
  • 查找单个匹配项:group
  • 查找单个匹配项:search
  • 查找单个匹配项:fullmatch
  • 匹配对象
  • 查找多个匹配对象——compile
  • 查找多个匹配对象——findall
  • 查找多个匹配对象——finditer
  • 分割——split
  • 替换——sub
  • 替换——subn
  • escape
  • purge
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档