专栏首页Python绿色通道万字长文详解Python正则表达式及re模块

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

大家好,偷学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就一直执行)

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

0317-1234567
010-1234567
0317-12345678
010-12345678
(010)1234567
0317 12345678

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

^\(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次,但尽可能少重复

分组

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

(?P<name>...)

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

(\d{1,3}\.){3}\d{1,3}

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

零宽断言

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

零宽度正预测先行断言

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

eating looking singing writing shopping

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

\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 ;注意它跟零长度匹配是不同的。语法格式如下:

re.match(pattern, string, flags=0)

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

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

"""
-*- 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;也可以为其命名使用,示例代码如下

"""
-*- 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是一样的,示例代码如下

"""
-*- 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 ;注意跟零长度匹配是不同的。语法格式跟上面的也是一样的,示例代码如下

"""
-*- 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 语句来判断是否匹配,示例代码

"""
-*- 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将正则表达式的样式编译为一个正则对象,可以用于匹配,语法结构?

re.compile(pattern, flags=0)

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

查找多个匹配对象——findall

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

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

查找多个匹配对象——finditer

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

"""
-*- 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方法按照能够匹配的子串将字符串分割后返回列表,语法结构

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

示例代码

"""
-*- 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用于替换字符串中的匹配项,语法结构如下

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

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

"""
-*- 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 中的特殊字符。例如正则里面的元字符,示例代码如下

"""
-*- 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()用于清除正则表达式的缓存。

本文分享自微信公众号 - Python绿色通道(Python_channel)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python爬虫:使用requests+re来爬取豆瓣图书

    这两天在知识星球上有球友在使用requests+re来爬豆瓣图书的链接,书名及作者遇到了问题,虽然当时很快给他解决了,但由于我之前没有写这方面的文章,所以临时决...

    叫我龙总
  • 爬虫篇 | Python使用正则来爬取豆瓣图书数据

    最近整理一个爬虫系列方面的文章,不管大家的基础如何,我从头开始整一个爬虫系列方面的文章,让大家循序渐进的学习爬虫,小白也没有学习障碍.

    叫我龙总
  • 薅羊毛 | Python 带你抢视频红包,不放过一个红包!

    如今短视频横行的时代,以某短视频为首的,背后依靠着强大的资金后盾,疯狂地对平台用户进行红包轰炸。

    叫我龙总
  • Python 爬虫必备-正则表达式(re模块)

    双愚
  • Python:爬虫系列笔记(6) -- 正则化表达(推荐)

    在前面我们已经搞定了怎样获取页面的内容,不过还差一步,这么多杂乱的代码夹杂文字我们怎样把它提取出来整理呢?下面就开始介绍一个十分强大的工具,正则表达式! 1.了...

    昱良
  • 简单的正则表达式

    特殊字符 ^ $ * ? + {2} {2,} {2,5} | [] [^] [a-z] . \s \S \w \W [\u4E00-\u9FA5] ...

    听城
  • Python学习(二) 正则表达式

    Python正则表达式 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re 模块使 Python 语言拥有全部的正则表达式功...

    深度学习思考者
  • python 历险记(六)— pytho

    刚接触正则表达式,我也曾被它们天书似的符号组合给吓住,但经过一段时间的深入学习,发现它并没有想象中那么可怕,只要多实践,多理解,也是可以轻松搞定的。 而且我发...

    py3study
  • python爬虫笔记之re.match匹配,与search、findall区别

    网上的定义【 从要匹配的字符串的头部开始,当匹配到string的尾部还没有匹配结束时,返回None;  当匹配过程中出现了无法匹配的字母,返回None。】 

    逆向小白
  • JDK1.7源码分析01-Collection

    同步发布:http://www.yuanrengu.com/index.php/20180221.html Java的集合类主要由两个接口派生而出:Collec...

    猿人谷

扫码关注云+社区

领取腾讯云代金券