目录
- 问题
- 解决方案
- 讨论
问题
如果使用正则化表达式,或者说如何通过准确定义正则化表达式的模式从而对字符串内容做准确的匹配?
解决方案
例如,在下述案例中,如何匹配出字符串文本 text_1
中双引号扩起的内容。
import repattern = re.compile(r'\"(.*)\"')
text_1 = 'Computer says "no."'
print(pattern.findall(text_1))
结果:
['no.']
问题,正则化匹配模式 \"(.*)\"
是什么含义?
\"
:首先因为我们需要提取出双引号中括住的内容,那么我们就需要对双引号进行识别。这里是一个反斜杠+双引号
的组合,因为在字符串中,双引号默认用于表示字符串的开始和结束。加入反斜杠后,双引号被转义,从而方便我们后续的识别需求。(.*)
:这里一个匹配组,捕获匹配的内容,其中,.
:这个英文句号,并不代表实际的句号含义,而是在正则化中,表示任意字符(除了换行符,除非指定DOTALL标志)。*
:这个星号意味着前面的字符(在这个案例中是任意字符)可以在后续出现 0 次或多次。
- 综合来讲,本匹配模式实现的是匹配出所有在双引号
""
中的内容。
但是很明显,这里容易出现一个问题,就是如果前后出现了两对双引号,那么结果将会是如何?
import repattern = re.compile(r'\"(.*)\"')
text_2 = 'Computer says "no." Phone says "yes."'
print(pattern.findall(text_2))
结果:
['no." Phone says "yes.']
正如我们预料的,确实将最开始的双引号一直到最后的双引号的内容囊括了出来。那么我们应该如何只识别出 ['no.', 'yes.']
内容。
import re# 新增一个? 将正则化匹配模式更改为不贪婪
pattern = re.compile(r'\"(.*?)\"')
text_2 = 'Computer says "no." Phone says "yes."'
print(pattern.findall(text_2))
在新的正则化匹配模式中,多了一个问号:\"(.*?)\"
.
:匹配除换行符以外的任意字符。*?
:表示非贪婪地匹配前面的字符零次或多次。非贪婪意味着它会匹配尽可能少的字符,而在这个例子中,即是会匹配被双引号包围的最短的字符串。
因此,其结果为结果:
['no.', 'yes.']
讨论
在本节中,一开始的匹配模式下包含句点字符(.
),其代表含义是匹配换行符之外的所有字符。但是,其默认是贪心原则,即会找出整段字符串中最开始以及最后的符合匹配模式的文本内容。
而在 *
或者 +
后添加一个 ?
,组成为 *?
或 +?
,则会强制将匹配算法调整为寻找最短的可能匹配结果。