上次课我们对字符组做了详细的介绍。 image.png 上次课中,在使用字符组简记法对电话号码进行匹配的代码如下:

import re

mo = re.search(r"\d\d\d-\d\d\d-\d\d\d\d", "My number is 415-555-4242")

print("Phone number found: " + mo.group())
1
2
3
4
5

虽然说使用字符组简记法对正则表达式进行了简化,但是我们发现还是有冗余。对于这种冗余可以使用量词来消除。使用量词可以将上面的代码改成如下形式:

import re

mo = re.search(r"\d{3}-\d{3}-\d{4}", "My number is 415-555-4242")

print("Phone number found:" + mo.group())
1
2
3
4
5

# 1.量词的一般形式

image.png 通过下面的一些例子大家可以感受下各个量词的用法:

# 1.1 {n}

import re

mo = re.search(r"a{3}", "aaaa")
mo.group()
1
2
3
4

上面代码中的 {3} 指示前面的 a 出现三次。

import re

mo = re.search(r"a{3}", "a")
print(mo == None)
1
2
3
4

# 1.2 {m,n}

import re

mo = re.search(r"a{3,6}", "aaaa")
mo.group()
1
2
3
4

上面代码中的 {3,6} 指示前面的 a 最少出现 3 次,最多出现 6 次。在 {3,6} 中,逗号后面不能有空格。

import re

mo = re.search(r"a{3,6}", "aa")
print(mo == None)
1
2
3
4

# 1.3 {m,}

import re

mo = re.search(r"a{3,}", "aaaa")
mo.group()
1
2
3
4

上面代码中的 {3,} 指示前面的 a 最少出现 3 次,出现次数没有上限。

import re

mo = re.search(r"a{3,}", "a")
print(mo == None)
1
2
3
4

# 1.4 {,n}

import re

mo = re.search(r"a{,3}", "aaaa")
mo.group()
1
2
3
4

上面代码中的 {,3} 指示前面的 a 可以出现,也可以不出现,最多出现 3 次。

import re

mo = re.search(r"a{,3}b", "b")
mo.group()
1
2
3
4

# 2.常用量词

{m,n} 是通用形式的量词,正则表达式还有三个常用量词,分别是+、?、* 。它们的形态虽然不同于 {m,n},功能却是相同的。 image.png 通过下面的一些例子大家可以感受下各个常用量词的用法:

# 2.1 *

import re

mo = re.search(r"a*b", "aaaab")
mo.group()
1
2
3
4

上面代码中的 * 指示前面的 a 可能出现,也可能不出现,出现次数没有上限。

import re

mo = re.search(r"a*b", "b")
mo.group()
1
2
3
4

# 2.2 +

import re

mo = re.search(r"a+b", "ab")
mo.group()
1
2
3
4

上面代码中的 + 指示前面的 a 至少出现 1 次,出现次数没有上限。

import re

mo = re.search(r"a+b", "aaaab")
mo.group()
1
2
3
4
import re

mo = re.search(r"a+b", "b")
print(mo == None)
1
2
3
4

# 2.3 ?

import re

mo = re.search(r"a?b", "aaab")
mo.group()
1
2
3
4

上面代码中的 ? 指示前面的 a 至多出现 1 次,也可能不出现。

import re

mo = re.search(r"a?b", "b")
mo.group()
1
2
3
4

# 3.滥用点号的问题

因为点号能匹配几乎所有的字符(换行符除外),所以实际应用中许多人图省事,随意使用 .* 或 .+,结果却事与愿违。例如下面例子中的正则表达式不但可以匹配正确的字符串也可以匹配不正确的字符串。

# 3.1 正确匹配

import re

mo = re.search(r"\".*\"","\"Great hopes\"")
mo.group()
1
2
3
4

上面的正则表达式正确匹配了 "Great hopes"。

# 3.2 不正确匹配

import re

mo = re.search(r"\".*\"","\"Great hopes\" make great men\"")
mo.group()
1
2
3
4

当用 .* 来匹配双引号字符串时,不但可以匹配正常的双引号字符串 "Great hopes",还可以匹配格式错误的字符串 "Great hopes" make great men"。这是为什么呢?答案是,正则表达式中的量词分为几类,之前介绍的量词都可以归到一类,叫做匹配优先量词(也叫贪婪量词)。匹配优先量词,顾名思义,就是在拿不准是否要匹配的时候,优先尝试匹配,并且记下这个状态,以备将来“反悔”。为了解决上述问题,正则表达式还提供了忽略优先量词。

# 4.忽略优先量词

和匹配优先量词相对应的,正则表达式还提供了忽略优先量词(也叫懒惰量词),如果不确定是否要匹配,忽略优先量词会选择“不匹配”的状态,再尝试表达式中之后的元素,如果尝试失败,再回溯,选择之前保存的匹配的状态。 image.png 忽略优先量词只需要在匹配优先量词的后面加上 ?即可。下面使用忽略优先量词再来进行上述字符串的匹配。

import re

mo = re.search(r"\".*?\"","\"Great hopes\"")
mo.group()
1
2
3
4
import re

mo = re.search(r"\".*?\"","\"Great hopes\" make great men\"")
mo.group()
1
2
3
4

在使用忽略优先量词之后,上面的两段代码都进行了正确的匹配。

# 5.小结

image.png

更新于: 12/30/2021, 2:46:39 AM