偷学Python第三十一天:Python正则表达式的语法以及re模块的使用 - Go语言中文社区

偷学Python第三十一天:Python正则表达式的语法以及re模块的使用


人生苦短我用Python

偷学Python第三十一天:Python正则表达式的语法以及re模块的使用

古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼

正则表达式的语法

什么是正则表达式

不管是使用Windows的搜索工具,还是在word文档里面的查找和替换,肯定都用过*?这种通配符,如果想要在某个目录下找到自己写的笔记,小甜最常用的方法就是*.md就可以找到当前文件夹下所有的Markdown文件了;正则表达式可以理解为超复杂的通配符,可以比通配符匹配的更为精准,正规一点的说法就是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。比如密码的限制(8位以上16位以内,不能出现特殊字符等等)、邮箱的格式,距离生活近一点的就是某手上的"你个萌萌",王者荣耀上的"你个**"。这些都是正则表达式的运用。

字符类

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

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

元字符

元字符说明正则表达式与之匹配的字符
.匹配除换行符以外的任意字符a1b
a2b
aab
a1b
a2b
aab
[^…]一个反向字符集a[^a]babb
a1b
a2b
w匹配字母、数字或者下划线。等价于 [A-Za-z0-9_]awba1b
a2b
aab
abb
s匹配任意空白字符asba b
d匹配数字。等价于 [0-9]1d31任意一个数字3
b匹配一个单词边界,也就是指单词和空格间的位置。babcb可以匹配abc
不能匹配abcd
^匹配字符串的开始用于限定一段内容的开始
$匹配字符串的结束用于限定一段内容的结束
W匹配除字母、数字或者下划线。等价于 [^A-Za-z0-9_]
S匹配任意非空白字符
D非匹配数字。等价于 [^0-9]
B匹配非单词边界

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

正则中的重复

语法说明
{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

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

正则表达式

^(0d{2,3})d{7,8}$|^0d{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)

image-20200530133554423

零宽度正回顾后发断言

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

栗子

image-20200530133957407

匹配re开头的单词不匹配re

此处用到的软件为*RegexBuddy*

负向零宽断言

零宽度负预测先行断言

(?!exp)零宽度负预测先行断言,断言此位置的后面不能匹配表达式exp,简单点说就是后面不能出现某个东东,

我们来查找包含Windows的字符串,但是Windows后面不能是10

image-20200530145533559

这里仅仅查找出来了3个

零宽度负回顾后发断言

(?<!exp) 零宽度负回顾后发断言,来断言此位置的前面不能匹配表达式exp

Python中提供的re模块使Python拥有全部正则表达式的功能

re模块

正则表达式的装饰符

修饰符描述完整名称
re.I使匹配对大小写不敏感re.IGNORECASE
re.Aw, W, b, B, d, D, sS 只匹配ASCII,而不是Unicodere.ASCII
re.L做本地化识别(locale-aware)匹配re.LOCALE
re.M多行匹配,影响 ^ 和 $,多行模式下是支持匹配行开头re.MULTILINE
re.S使 . 匹配包括换行在内的所有字符e.DOTALL
re.U根据Unicode字符集解析字符。这个标志影响 w, W, b, B.re.UNICODE
re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。re.VERBOSE

查找单个匹配项

match

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]+sw+"  # 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]+sw+"
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"bw+b"
pattern_object = re.compile(r"bw+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', 'pythonnjavascriptnLinuxn']

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'ws*dd.'
# 打印pattern的特殊字符
print(re.escape(pattern))  # \w\s*\d\d.

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

purge

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

总结

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_46163658/article/details/106447424
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢