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。

相关推荐
天天被压力2 分钟前
【A股分钟级高频数据完全手册 #11】券商实时行情数据接口:实时交易(券商源)接入与字段
java·人工智能·python
总裁余(余登武)6 分钟前
【python项目】:1操作EXCEl自动化开发
python·自动化·excel
芝梵家电数据笔记9 分钟前
用字段表核对暖菜板长期使用条件:结构、暖区、质保边界
python·家电·数据校验·dataclass·选购
天天被压力15 分钟前
【A股分钟级高频数据完全手册 #12】股票五档盘口数据接口:买卖五档实时盘口解析
java·人工智能·python
卷无止境13 小时前
RAGFlow 项目全景报告:从文档解析引擎到 Agent 原生数据库
后端·python
灿飞屋14 小时前
Kotlin与Java虚拟机三:控制流
android·java·开发语言·后端·python·kotlin·安卓
benchmark_cc14 小时前
单只股票五档盘口接入个人研究仓库:QuantDash 读取、校验与易错点
python·数据分析·pandas·量化交易·股票数据·quantdash
青少儿编程课堂16 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:中缀表达式求值解析
c++·python·算法·bfs·信息学竞赛
灿飞屋18 小时前
Kotlin与Java虚拟机四:函数
java·python·kotlin·安卓·android jetpack
一晌小贪欢19 小时前
Python基础:第 09 篇 · while 循环:重复直到不满足
开发语言·c++·python·数据可视化·面向对象·python办公