python-字符串全解(六):正则表达式-量词

本章讨论正则表达式的量词,通过量词你可以匹配不同长度的重复字符类型,从而解决非定长的字符串的匹配。

4,量词

量词说白了就是如何表示重复,这里的重复不只是单个字符的重复,还包括不同字符的重复,例如数字0-9任意一个字符的重复。同时,还有贪婪和非贪婪的区别,接下来我们来一一说明。

4.1零次或者多次(*)

通过*跟在一个字符或者字符类后面,可以实现0次或者多次重复。我们看下下面的例子:

python 复制代码
import re
a = "a12bca1234bcabc"
a1 = "abcbcbc"

b = re.findall(r'a[0-9]*bc', a)
c = re.findall(r'a[bc]*c', a1)
d = re.findall(r'a[bc]*?c', a1)

print("a= {}, b = {}, c = {}, d = {}".
      format(a, b, c, d))
# 输出结果:a= a12bca1234bcabc, b = ['a12bc', 'a1234bc', 'abc'], 
# c = ['abcbcbc'], d = ['abc']

a通过在0-9的后面跟一个*,表示这个0-9的任意数字出现0次或者多次,所以符合条件的有三个子串,第一个子串a12bc,数字出现了两次,第二个子串a1234bc,数字出现了4次,第三个是abc,数字出现了0次,因为*代表0次或者多次,所以abc也符合条件。

c通过bc*表示b或者c出现0次或者多次,我们发现匹配到了整个字符串abcbcbc。但是其实abc,abcbc这两个子串也符合条件,但是为什么匹配的是最多的字符的呢?这涉及到正则表达式的一个默认原则,贪婪匹配,就是尽量匹配更多的字符,按照这个原则,整个字符串都被匹配进来了。

有时候,我们不想匹配那么多,只想匹配满足条件的最少字符的字符串,那么这就用到了非贪婪匹配的语法。就是*后面加上一个?,这就告诉正则表达式,尽量在满足条件的情况下少匹配字符。就像d这样,我们只匹配了一个abc。

4.1.1贪婪与非贪婪

根据上面的代码,我们引出了贪婪和非贪婪的概念。在正在表达式中如果应用了不确定次数的匹配,比如*,那么正则表达式会尽量匹配多的字符,这就是贪婪匹配。如果你不想做贪婪匹配,就需要在后面加上?,表示尽量少的匹配字符。除了*,还有其他几个字符后面可以根?表示非贪婪,后面我们会一一谈到。因为第一个讲到的就是*,所以我们就先引入贪婪和非贪婪的概念,让你一开始就不带着疑问去看,虽然排版上看上去比较凌乱,但是不会让你带着疑问往下看,这是值得的。后面遇到可以根非贪婪字符?的情况,我们都会讲到。

4.2一次或者多次(+)

这个+和*唯一的区别就是+表示至少出现一次,而*可以出现0次。我们看下下面的代码:

python 复制代码
import re
a = "a12bca1234bcabc"
a1 = "abcbcbcac"

b = re.findall(r'a[0-9]+bc', a)
b1 = re.findall(r'a[0-9]*bc', a)
c = re.findall(r'a[bc]+c', a1)
c1 = re.findall(r'a[bc]*c', a1)
d = re.findall(r'a[bc]+?c', a1)
d1 = re.findall(r'a[bc]*?c', a1)
print("a= {}, b = {}, b1={},  c = {}, c1 = {}, d = {}, d1 = {}".
      format(a, b, b1, c, c1, d, d1))
# 输出结果:a= a12bca1234bcabc, b = ['a12bc', 'a1234bc'], 
# b1=['a12bc', 'a1234bc', 'abc'],c = ['abcbcbc'], 
# c1 = ['abcbcbc', 'ac'], d = ['abc'], d1 = ['abc', 'ac']

a1和之前代码不同的地方上我在后面又加了一个ac。

b通过0-9+表示从0到9至少出现一次,所以满足条件的只有a12bc和a1234bc这两个子串。对比b1,多了个abc,因为*允许数字出现0次。

c和d的结果一样,因为+也是默认贪婪,都是尽量的多匹配字符。

d和d1分别表示了*和+的非贪婪匹配,都是在后面加了?号。因为*允许出现0次,所以最后的ac也会被匹配。因为非贪婪,所以和c,c1的匹配结果完全不同。

4.3 0次或1次(?)

这个?和上面我们讲的在*或者+后跟的?是不一样的。这个?代表前面的字符只出现0次或者1次。当然,它也有对应的非贪婪的写法,就是后面跟?,也就是两个??。我们看一下代码:a

python 复制代码
import re
a = "a2bca1234bcabc"
a1 = "abcbcbcacc"

b = re.findall(r'a[0-9]?bc', a)
c = re.findall(r'a[bc]?c', a1)
d = re.findall(r'a[bc]??c', a1)

print("a= {}, b = {}, c = {}, d = {}".
      format(a, b, c, d))
# 输出结果:a= a2bca1234bcabc, b = ['a2bc', 'abc'], 
# c = ['abc', 'acc'], d = ['abc', 'ac']

a可以看到正则表达式中0-9?表示在a和c之间出现0个或者一个数字,满足条件的字符串子串有a2bc以及abc。

c验证了?的默认的贪婪模式,我们可以看到匹配了acc子串,d我们用了??来非贪婪匹配,结果匹配了ac而不是acc。

4.4 确定次数({n})

上面我们学习了*,+和?都是出现不确定的次数。假如我们需要出现准确的次数,我们可以通过花括号中间填需要出现的次数来进行约束。例如我想字符a出现五次,那么可以直接在a后面跟{5}。我们看下面的例子:

python 复制代码
import re
a = "a2bca1234bcabc"
a1 = "abcbcbcacc"

b = re.findall(r'a[0-9]{4}bc', a)
c = re.findall(r'ac{2}', a1)


print("a= {}, b = {}, c = {}".
      format(a, b, c))
# 输出结果:a= a2bca1234bcabc, b = ['a1234bc'], c = ['acc']

b中设定了数字出现4次,所以只匹配到了a1234bc,c中设定字符c出现两次,所以匹配到了acc。这种经常用到明确知道固定字符数的场景,就好比我们之前的电话号码:r"13-9\d{9}"。

4.5从m到n次({m,n})

{m,n}代表一个范围,表示至少出现m次,至多出现n次。对于不确定的次数,所以我们很自然而然的想到贪婪和非贪婪。我想经过上面的学习,大家很容易想出来,非贪婪的写法就是在后面加?。我们看看下面的代码:

python 复制代码
import re
a = "a2bca1234bcabc"
a1 = "abcbcbcacc"

b = re.findall(r'a[0-9]{1,4}bc', a)
c = re.findall(r'a.{0,9}c', a1)
d = re.findall(r'a.{0,9}?c', a1)

print("a= {}, a1= {}, b = {}, c = {}, d = {}".
      format(a, a1, b, c, d))
# 输出结果:a= a2bca1234bcabc, a1= abcbcbcacc, 
# b = ['a2bc', 'a1234bc'], c = ['abcbcbcacc'], 
# d = ['abc', 'ac']

b中,a和bc之间的数字需要出现至少1次,最多4次,所以匹配到了a2bc和a1234bc。

c是贪婪式匹配,所以匹配到了整个字符串。d因为花括号后面加了?,所以进行非贪婪的匹配,所以匹配到了两个子串。注意这次我们用了通配符逗号,其实也可以用我们之前学到的\w来匹配单词字符,至少对这个例子来讲效果是一样的。

4.6 至少({m,})和最多{,n}

这个和上面学到的从m次到n次是一样的,只是一个限定了最少次数,一个限定了最多次数。同样,既然有不确定的次数,所以必然会有贪婪和非贪婪的区别,只要在后面加?即可。我们看下面的代码:

python 复制代码
import re
a = "a2bca1234bcabc"
a1 = "abcbcbcacc"

b = re.findall(r'a[0-9]{1,}bc', a)
c = re.findall(r'a.{,9}c', a1)
d = re.findall(r'a.{,9}?c', a1)

print("a= {}, a1= {}, b = {}, c = {}, d = {}".
      format(a, a1, b, c, d))
# 输出结果:a= a2bca1234bcabc, a1= abcbcbcacc,
# b = ['a2bc', 'a1234bc'], c = ['abcbcbcacc'],
# d = ['abc', 'ac']

上面的代码只是稍微的做了修改,和4.5的结果是一样的。我们将{1,4}改为{1,},将{0,9}改为{,9},注意这些数字,逗号,以及花括号之间不能有空格,要不然会不是你想要的结果,有兴趣的同学可以试一下。

到这里,我们量词就讲完了,由于正则表达式太过于复杂,所以我们每章的内容都不会太长。如果你读到这里,我强烈建议你复习一下前面学到的章节,以免遗忘。下一章我们讲位置断言,到时候你会再次碰到我们熟悉的\b。

相关推荐
禹凕1 小时前
机器学习之Selenium(Machina Learning about Selenium)
爬虫·python·selenium·测试工具·机器学习
ebiobiz3 小时前
基于 GD32 Embedded Builder (GEB) 与 Nimmake 的 MCU 工程搭建指南
c++·python·单片机·嵌入式硬件·mcu
老歌老听老掉牙3 小时前
穿越时空的物流密码:VRP与VRPTW的深度剖析及实战求解
python·物流·vrp
FYKJ_20104 小时前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
ShyanZh4 小时前
【Python3基础】13-Python 常用设计模式
开发语言·python·设计模式
小静AI工程实验室4 小时前
RSA 算法详解:从模幂原理到 OAEP 加密、PSS 签名与 Python 实验
python·算法
xzal124 小时前
GESP Python笔记
笔记·python·算法
半日闲dusknook5 小时前
我给 AI 写了个「音频编译器」:把「听起来还行」变成 PASS/FAIL
python
在世修行5 小时前
干货:信号槽
python·信号槽