文章目录
1 问题
在尝试使用正则表达式进行文本匹配时,你发现匹配出了可能的字符串中最长的字那个,但实际上你希望匹配出最短的那个。
2. 解决方案
上述问题通常发生在这样的情况下,即尝试匹配的文本被包含在了一对起始和终止分隔符之间(例如:一对双引号之间的文本)。例如:
>>> import re
>>> str_pattern = re.compile(r'\"(.*)\"')
>>> text1 = 'Computer says "no."'
>>> re.findall(r'\"(.*)\"', text1)
['no.']
>>> str_pattern.findall(text1)
['no.']
>>> text2 = 'Computer says "no." Phone says "yes."'
>>> re.findall(r'\"(.*)\"', text2)
['no." Phone says "yes.']
>>> str_pattern.findall(text2)
['no." Phone says "yes.']
在上述案例中,对于 text2
,你可能希望匹配出 no.
以及 yes.
,而由于正则表达式 r'\"(.*)\"'
的含义是匹配在一对双引号之间除了换行符以外(在正则表达式中 .
表示除换行符以外的字符)的字符出现零次或任意多次(在正则表达式中 *
表示其之前的字符串出现零次或任意多次),即 *
的功能具有贪婪特征,因此最终匹配到的是最长结果。
为了避免这样的情况发生,只需要在 *
之后加上 ?
修饰符即可,例如:
>>> re.findall(r'\"(.*?)\"', text2)
['no.', 'yes.']
>>> str_pattern = re.compile(r'\"(.*?)\"')
>>> str_pattern.findall(text2)
['no.', 'yes.']
这就使得最终的匹配结果变为非贪婪的模式。
3. 讨论
本文提及的在使用 .
构造正则表达式的情况下比较常见,由于 .
在正则表达式中可以表示除换行符外的任意字符,因此在待匹配的文本中,中间的起始和结束分隔符(例如本文中的双引号)将会被视为成功匹配的字符串的一部分。