python详解(9)——进阶(3):正则表达式,这一篇就够了!

目录

🏆一、前言

🏆二、正则表达式简介

🏆三、内置函数

🚩1、查找一个匹配项

🚩2、替换匹配项

🚩3、查找多个匹配项

🚩4、分割匹配项

🏆四、语法-基础

🚩1、特殊字符*?+.

🚩2、[ ]与-

🚩3、^和$

🚩4、{ }

🚩5、非打印字符

🚩6、( )和|

🚩7、\b \B \w \W \s \S \d \D

🚩8、表达式练习

🏆五、语法-进阶

🚩1、贪婪与非贪婪

🚩2、零宽断言

🚩3、反向引用

🏆六、尾声

🏆一、前言

上一篇文章,我们已经学习了算法的6个方法。这一篇文章,我们来学习正则表达式。

写作不易,支持一波~


🏆二、正则表达式简介

360百科是这样说的:

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。

(字符序列就是字符串)来看看创作助手是怎么说的:

Python正则表达式是一种强大的字符串匹配工具,可以用于搜索、替换和提取文本中的特定模式。通过使用正则表达式,可以快速有效地处理文本,并且可以轻松处理更加复杂的数据,如网络日志、HTML和XML文件等。

总之,正则表达式是用来匹配一个字符串的。

比如说,我们在设置QQ密码的时候,有这么几条规则:

1、字母数字都要有

2、长度为9-16位(好像是)

3、只能有数字字母,不能有其他的

这四条规则可以用一条指令代替,后面会学。这条指令就叫做正则表达式。

如果输入的字符串可以匹配这四个正则表达式,则可以通过。

比如说字符串:1145141919810、abcdefghijkl,abc1111111111111111111,!@#¥%……&*(),他们都不可以成功匹配,我们就说这些字符串不能成功匹配这个正则表达式。

像aaa12345678就可以。我们就说aaa12345678可以匹配这个正则表达式。

另外,字符串也可以是正则表达式。比如最近某人塌了,某站要屏蔽所有和这个人有关的视频,就可以设置这些字符串:

" 只 因 你 太 美 "
" 鸡 你 太 美 "
" 蔡 徐 坤 "
" 你 干 嘛 "
" 小 黑 子 "
" 真 爱 粉 "
······

(希望这篇文章不会被CSDN屏蔽哈哈哈哈哈)这些字符串也可以是正则表达式。如果视频标题包含这些正则表达式,就进行屏蔽处理。十分的食用。

正则表达式就是re模块,属于内置,可以直接用。

接下来我们一边学里面的函数一边了解:


🏆三、内置函数

re模块有12个内置函数,我们只学习8个。

🚩1、查找一个匹配项

如果要查找一个匹配项,有3个函数。

👍①:match

这个函数,是用来匹配字符串的开头符不符合规则的。匹配成功返回匹配成功的对象,匹配失败返回None。格式如下:

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

pettern:正则表达式

string:被匹配的字符串

flags:匹配模式。填re.I为对大小写不敏感,填re.M为多行模式,re.X忽略空格和注释(仅有增强可读性的作用)。

来看实例:

import re 
print(re.match("py","python").span())
print(re.match("py","666"))
print(re.match("py","PYTHON",re.I).span())#不分大小写

输出:(0,2)

None

(0,2)

这个 .span() 有啥用呢?如果第一行不加 .span() ,返回的是:

<re.Match object; span=(0, 2), match='py'>

这个东西就是匹配成功后返回的对象。 .span() 就是返回对象里面的span=(0, 2)这一项。

我们可以看出, .span() 的作用就是返回要匹配的字符串在被的字符串里面的位置。

如果第二行加上.span(),返回的是:

AttributeError: 'NoneType' object has no attribute 'span'

因为返回的是None,所以没有span就会报错。

👍②、re.search

他的用法和match一样,只不过match只检查开头是否匹配,search检查任意位置是否匹配,其他的都一样,不再多说。

👍③、re.fullmatch

用法和match一样,但是fullmatch检查的是字符串是否完全匹配正则表达式,其他的都一样。

🚩2、替换匹配项

如果我们要替换匹配项,就要用到sub和subn。

👍①:re.sub

它的作用是:替换字符串的匹配项,返回替换完的字符串。先看格式:

re.sub(pattern, repl, string, count=0, flags=0)

pattern:要匹配的正则表达式

repl:要替换的字符串

string:要匹配的字符串

count:替换的最大次数,0表示把所有的都匹配

flags:匹配模式,见re.match一栏

程序实例:

import re 
print(re.sub("12","**","12345678"))
print(re.sub("123","***","123123123123",2))#count=2,只能替换掉两个123
print(re.sub("999","***","999999999"))#count=0,默认替换全部

输出:**345678
******123123
*********

因为sub的返回值是替换完毕的字符串,所以不用加span。

另外,参数repl还可以是一个函数,获得的是函数的返回值。

import re 
def repl():
    return "修改"
print(re.sub("12",repl(),"12,12,12,12,12"))

输出:修改,修改,修改,修改,修改 

👍②:re.subn

他和sub差不多, 差别在于他的返回值是一个元组:(修改后字符串,替换的次数)

🚩3、查找多个匹配项

如果我们想要查找一个或者多个匹配项,有两个函数:findall,finditer。

👍①、re.findall

它会获取字符串里所有的匹配项,返回组成的列表。参数还是那几样。程序实例:

import re 
print(re.findall("12","12,12,12,12,12"))

输出: ['12', '12', '12', '12', '12']

👍②、re.finditer

他和re.findall只有返回值上的差异:finditer返回的是一个迭代器。

迭代很少见到,大家是不是都忘了怎么用了?

import re 
a=re.finditer("12","12,12,12,12,12")
print(next(a))
print(next(a))
print(next(a))

输出: <re.Match object; span=(0, 2), match='12'>
<re.Match object; span=(3, 5), match='12'>
<re.Match object; span=(6, 8), match='12'>

注意:这个迭代器返回的是迭代器,想要获取位置就在后面加 .span()。

🚩4、分割匹配项

如果想要分割匹配项,只有一个函数:re.split()。

他可以将以正则表达式为界,将字符串分割,组合成一个列表。

例如:正则表达式为"1",字符串为"22213331444",他的返回值就是["222","333","444"]

参数:

re.split(pattern, string, maxsplit=0, flags=0) 

pattern:正则表达式

string: 要匹配的字符串

maxsplit:分割最大次数,0表示全部分割

flags:匹配模式,见re.match一栏

实例:

import re 
print(re.split("1","222133314441555"))
print(re.split("1","222133314441555",2))

输出: ['222', '333', '444', '555']
['222', '333', '4441555']


🏆四、语法-基础

🚩1、特殊字符*?+.

现在,大家应该对正则表达式有了一个初步的认识。再回头去看上面的正则表达式简介,你会有一个新的认识。

其实,正则表达式也是有语法的,就像这样:

匹配电话号码:^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$

字母开头、只能数字字母下划线、长度6-18的密码:^[a-zA-Z]\w{5,17}$

我们来学习一下。他可以更方便地设置正则表达式。建议收藏!

首先,大家应该还记得字符串前面加r是什么。他可以让\后面的字母不与其发生转义。

像\n,\b这样的字符叫做非打印字符,后面要考,记住!

例如:

a="a\nb"
b=r"a\nb"
print(a)
print("----------")
print(b)

输出:a
b
----------
a\nb

字符串前面加个r,\n就不会发生转义。

但是,如果这个字符串是一个正则表达式,就不一样了。当这个字符串是一个正则表达式的时候,r就用来设置语法。

我们先学一下最基础的三个4符号: +?*.

问号:问号前面的字符有没有都行。就像这样:

import re
print(re.search(r"colou?r","colour").span())
print(re.search(r"colou?r","color").span())

问号前面的u加不加都行。

加号:加号前面的字符,出现几次都行,但是不能出现0次。

import re
print(re.search(r"1234+5","123444444444445").span())
print(re.search(r"1234+5","1235"))

输出:(0, 15)
None

乘号:乘号前面的字符出行几次都行,0次也行。

import re
print(re.search(r"1234*5","123444444444445").span())
print(re.search(r"1234*5","1235").span())

输出:(0, 15)

(0, 4)

点有什么用呢?他可以匹配任意字符(换行符除外),就是一个占位的。

import re
print(re.search(r".....","啊6*&#").span())
print(re.search(r".....","啊啊啊"))

输出:(0, 5)
None 

🚩2、[ ]与-

然后,我们来学中括号。

中括号里的字符,每一个都能进行匹配。

import re
print(re.findall(r"[0123456789]","啊啊啊5啊啊啊"))
print(re.findall(r"[0987654321]","啊啊114514啊啊"))

输出:['5']
['1', '1', '4', '5', '1', '4']

在中括号里面填入0-9的数字,就意味着只要是数字就能进行匹配。

如果我们在左中括号的右边加一个^符号,就意味着除了这些内容,都能进行匹配。

import re
print(re.findall(r"[^1234567890]","啊啊114514啊啊"))

输出:['啊', '啊', '啊', '啊']

这个程序,就是除了数字以外,全部都能进行匹配。

另外,我们还可以这样:

[0-9]:匹配0123456789

[A-Z]:匹配ABCDEFGH······UVWXYZ。

[a-z]:匹配abcedfg·······uvwxyz。

像[k-z],[1-9]等等的匹配也是可以的。例如:

import re
print(re.findall(r"[0-9]","啊啊114514啊啊"))
print(re.findall(r"[a-z]","啊啊啊abcd"))
print(re.findall(r"[k-z]","abcxyz"))

输出:['1', '1', '4', '5', '1', '4']
['a', 'b', 'c', 'd']
['x', 'y', 'z']

这就是中括号的用法。

🚩3、^和$

接下来讲^和$.

如果我们想只匹配开头,就在开头加^.

import re
print(re.search(r"^123","123456").span())
print(re.search(r"^123","456123"))

输出:(0, 3)
None

如果想只匹配结尾,就在结尾加$。

import re
print(re.search(r"123$","456123").span())
print(re.search(r"123$","123456"))

输出:(3, 6)
None

如果两个都加,就是看是否完全匹配。

import re
print(re.search(r"^123$","123").span())
print(re.search(r"^123$","123123"))

输出:(0, 3)
None

🚩4、{ }

接下来学习大括号。

大括号,就表示前面的字符可以出现的次数。

{n},代表前面的字符只能出现n次,不能多不能少。

import re
print(re.fullmatch(r"1{2}","11").span())
print(re.fullmatch(r"1{2}","1"))

输出:(0, 2)
None

{n,},代表前面的字符至少出现n次,只能多不能少。

import re
print(re.fullmatch(r"1{2,}","1111111").span())
print(re.fullmatch(r"1{2,}","1"))

输出:(0, 7)
None

{,n}也是可以的,代表前面的字符最多出现n次,不能多只能少。代码就不放了。

{m,n},代表前面的字符最少出现m次,最多出现n次。

import re
print(re.fullmatch(r"1{2,3}","11").span())
print(re.fullmatch(r"1{2,3}","1111"))

输出:(0, 2)
None

另外,{0,1}等价于?。他们两个用法一样。那么请问:*和+分别与什么等价?

答案就是{1,}和{0,} 。

🚩5、非打印字符

非打印字符也可以互相匹配。直接看图片吧

 来源:菜鸟编程-正则表达式

🚩6、( )和|

中大括号都有了,小括号肯定也有用。

它的作用很简单:括起来。

比如 r"abcd?" ,这里的问号只能对d奏效。

r"ab(cd)?",这里的问号就可以对cd奏效了。

小括号的作用就是捕获分组。有点难理解的名词?那就看着实例学会它:

import re
print(re.fullmatch(r"ab(cd)?","ab").span())
print(re.fullmatch(r"ab(cd)?","abc"))

输出:(0, 2)
None

这里,我们将cd扩起来,问号就对cd奏效。

import re
print(re.fullmatch(r"(cd)*","cdcdcdcdcd").span())
print(re.fullmatch(r"(cd)*","cdddddd"))

输出:(0, 10)
None

这里的*号也对cd奏效,所以要cdcdcd······才能匹配。

这样大家应该能理解了。

|的意思就是或者。a|b就是a或者b都能匹配。

import re
print(re.fullmatch(r"a|b","a").span())
print(re.fullmatch(r"a|b","b").span())

输出:(0, 1)
(0, 1)

🚩7、\b \B \w \W \s \S \d \D

\b的作用是匹配单词边界,\B的作用是匹配除了\b以外的字符。

单词边界是什么?接着看例子来理解。

import re
print(re.search(r"nm\b","cnm").span())
print(re.search(r"nm\b","nmsl"))

输出:(1, 3)
None

这下应该能理解了吧?只有匹配的是一个单词的边界才行。\B就是匹配除了\b之外符合条件的。

\w就是匹配数字字母下划线,\W就是匹配除了\w之外的。问题来了:\w和\W分别等价于什么?

\w等价于[a-zA-Z0-9_],\W等价于[^a-zA-Z0-9_]。

\s的作用,就是匹配空格、tab、换行符、回车符等等。\S也是匹配除了\s以外的字符。

\d的作用是匹配数字,等价于[0-9]。\D匹配非数字,等价于[^0-9]。

🚩8、表达式练习

这就是全部的正则表达式语法了。

接下来,我们开始练习:根据要求,写出正确的正则表达式。

1、完全匹配正数

简单的[0-9]+是不行的,因为会出现03、06、0这样的数。

正数,他的第一位必须是1-9,第二位可以没有,如果有则可以是0-9,第三位也是······所以,匹配正数的方法就是: [1-9][0-9]* 。完全匹配,还要加上^和$。[0-9]可以用\d代替,最终结果:

^[1-9]\d*$

2、匹配正数、负数、小数

匹配示例:

+123可以匹配,-11.11可以匹配,3.0可以匹配,+-33不可以匹配,0可以匹配,03不可以匹配。

这个有点难。我们要考虑到所有情况。

^[+-]?(0|[1-9]\d*)(\.\d+)?$ 

先看结果,我们来挨个挨个解析。

首先,^和$已经讲过了,是完全匹配字符串的作用。

[+-]? 这里,是为了让前面有正号或者负号。

有人问了:它是怎么做到 +-33 不可以匹配的?

这是因为 [+-] 只对应了一个字符,所以它匹配的只是 +-33 中的 + ,至于 - 就放着不管了。

这里还好理解,来看下部分: (0|[1-9]\d*)

我们先了解一下优先级。创作助手是这样说的:

简单来说,这串表达式中,[1-9]\d* 是优先的。所以这串表达式中, [1-9]\d* 是一体的。

他的意思就是:匹配 0 或者 [1-9]\d* 。 [1-9]\d* 已经讲过了,是匹配所有正数的意思。

然后来看 (\.\d+)? ,他的意思很简单,就是一个小数点,加上1或多位的数字。括起来,加个问号表示可有可无。

为什么小数点前面要加\呢?这是因为上文说了,小数点他可以代替任何字符。只要在前面加上反斜杠,他就能作为一个普通的字符小数点使用。

最终结果:^[+-]?(0|[1-9]\d*)(\.\d+)?$  代码:

import re
while True:
    a=input()
    b=re.fullmatch(r"^[+-]?(0|[1-9]\d*)(\.\d+)?$",a)
    if b:
        print("匹配成功")
    else:
        print("匹配失败")

3、汉字

这不是给你们的练习题,这是得记下来的:

[\u4e00-\u9fa5]+

\u4e00代表了第一个汉字“一”,\u9fa5代表了最后一个汉字齄。这个范围内则表示匹配所有汉字。

import re
print(re.findall(r"[\u4e00-\u9fa5]+","一1二2三3四4"))

输出:['一', '二', '三', '四']


🏆五、语法-进阶

🚩1、贪婪与非贪婪

正则表达式匹配模式分为贪婪和非贪婪。举个例子:

一个正则表达式:\d+,用来匹配正数。现在要findall字符串:"12345"。

贪婪匹配,匹配的是['12345']

非贪婪匹配,匹配的是['1','2','3','4','5']

从上述例子,我们能看出来:贪婪匹配就是能匹配多少就匹配多少,非贪婪匹配就是只匹配一个。

一般来说,正则表达式是默认贪婪匹配的,能匹配多少就匹配多少。

如果想要变成非贪婪模式,只要在量词后面加个问号就行。量词就是 *?+{} 这四个。问号后面加问号也是可以的。

import re
print(re.findall(r'\d+','12345'))
print(re.findall(r'\d+?','12345'))#非贪婪模式

输出:['12345']
['1', '2', '3', '4', '5']


🚩2、零宽断言

名字听起来是不是有些奇怪?它的作用呢···嗯···就是匹配位置限制。

比如我要匹配字符串an apple,我们只想获取里面的apple,但又不想匹配到a apple,就要用到零宽断言。

零宽断言符号有四种:?=  ?!  ?<=  ?<!

?=,它叫做正向先行断言。先看例子:

import re
print(re.findall(r'\d+(?=元)','12345元'))
print(re.findall(r'\d+(?=元)','12345美元'))

输出:['12345']
[]

这里,我们在后面加上了 (?=元) ,意思是看这个字符串的后面是不是“元”,如果是则匹配,不是则不匹配。

?!,它叫做负向现行断言,和?=是相反的。如果不是则匹配,是反而不匹配。比如上述例子里面,如果把?=换成?!,那么第二个则会返回['12345'],第一个返回[]。

?<=,它叫做正向零宽回顾后发断言。

名字很长?其实就是将括号的位置和匹配的位置从后面挪到了前面。

import re
print(re.findall(r'(?<=123)个','123个'))
print(re.findall(r'(?<=123)个','456个'))

输出:['个']
[]

这里,如果匹配到“个”,并且前面是数字123就匹配成功。

?<!,叫做负向零宽回顾后发断言。看名字就知道他是怎么用的了。他继承了?<=和?!的优良基因。这里不再讲解。


🚩3、反向引用

反向引用是用来帮助匹配重复的,也叫回溯引用。先看代码:

import re
print(re.search(r'(hello)\s\1','hello'))
print(re.search(r'(hello)\s\1','hello hello'))

输出:None
<re.Match object; span=(0, 11), match='hello hello'>

我们挨个挨个看。

(hello)就是用圆括号捕获分组。\s就是正常匹配空格。

\1,他就是反向引用。它的作用就是引用这个表达式里的第一个圆括号,这里就是字符串hello。

所以,这个表达式就等价于(hello)\shello。

再来看一个例子:

import re
print(re.fullmatch(r'(hello)\s\1\s\1','hello hello'))
print(re.fullmatch(r'(hello)\s\1\s\1','hello hello hello'))

输出:None
<re.Match object; span=(0, 17), match='hello hello hello'>

这就说明第一个字符串没有成功完全匹配。

这是因为,这个正则表达式等价于(hello)\shello\shello,所以第一个匹配失败。

如果\1替换为\2,他的意思就是引用第二个圆括号。\3,\4同理。如果没有就会报错。

再来看例子:

import re
print(re.fullmatch(r'([ab])\s\1','a a'))
print(re.fullmatch(r'([ab])\s\1','a b'))

输出:<re.Match object; span=(0, 3), match='a a'>
None

为什么第二个会输出None?我们来分析一下。

\1引用的是[ab]。所以如果[ab]匹配的是a,那么\1也应该是a。如果[ab]匹配的是b,那么\1也应该是b。

所以,这个表达式只能匹配 a a 和 b b ,不能匹配 a b 和b a 。


🏆六、尾声

我去,怎么又过了一个月才写一篇文章······

写作不易,求个三连支持~


  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值