正则表达式——常用量词

{m,n}是通用形式的量词,正则表达式还有三个常用量词,分别是+、?、*。它们的形态虽然不同于{m,n},功能却是相同的(也可以把它们理解为"量词简记法"),具体说明见表2-2。

表2-2  常用量词

常用量词

{m,n}等价形式

说明

*

{0,}

可能出现,也可能不出现,出现次数没有上限

+

{1,}

至少出现1次,出现次数没有上限

?

{0,1}

至多出现1次,也可能不出现

在实际应用中,在很多情况只需要表示这三种意思,所以常用量词的使用频率要高于{m,n},下面分别说明。

大家都知道,美国英语和英国英语有些词的写法是不一样的,比如traveler和traveller,如果希望"通吃"traveler和traveller,就要求第2个l是"至多出现1次,也可能不出现"的,正好使用?量词:travell?er,如例2-4所示。

例2-4  量词?的应用

re.search(r"^travell?er$", "traveler") != None  #  => True  
re.search(r"^travell?er$", "traveller") != None #  => True  

其实这样的情况还有很多,比如favor和favour、color和colour。此外还有很多其他应用场合,比如http和https,虽然是两个概念,但都是协议名,可以用https?匹配;再比如表示价格的字符串,有可能是100也有可能是¥100,可以用¥?100匹配 。

量词也广泛应用于解析HTML代码。HTML是一种"标签语言",它包含各种各样的tag(标签),比如<head>、<img>、<table>等,这些tag的名字各异,形式却相同:从<开始,到>结束,在<和>之间有若干字符,"若干"的意思是长度不确定,但不能为0(<>并不是合法的tag),也不能是>字符 。如果要用一个正则表达式匹配所有的tag,需要用<匹配开头的<,用>匹配结尾的>,用[^>]+匹配中间的"若干字符",所以整个正则表达式就是<[^>]+>,程序如例2-5所示。

例2-5  量词+的应用

re.search(r"^<[^>]+>$", "<bold>") != None       #  => True  
re.search(r"^<[^>]+>$", "</table>") != None     #  => True  
re.search(r"^<[^>]+>$", "<>") != None           #  => False  

类似的,也可以使用正则表达式匹配双引号字符串。不同的是,双引号字符串的两个双引号之间可以没有任何字符,""也是一个完全合法的双引号字符串,应该使用量词*,于是整个正则表达式就成了"[^"]*",程序见例2-6。

例2-6  量词*的应用

re.search(r"^\"[^\"]*\"$", "\"some\"") != None  #  => True  
re.search(r"^\"[^\"]*\"$", "\"\"") != None      #  => True

注:字符串之中表示双引号需要转义写成\",这并不是正则表达式中的规定,而是为字符串转义考虑。

量词的使用有很多学问,不妨多看几个tag匹配的例子:tag可以粗略分为open tag和close tag,比如<head>就是open tag,而</html>就是close tag;另外还有一类标签是self-closing tag,比如<br/>。现在来看分别匹配这三类tag的正则表达式。

open tag的特点是以<开头,然后是"若干字符"(但不能以/开头),最后是>,所以对应的正则表达式是<[^/][^>]*>;注意:因为[^/]必须匹配一个字符,所以"若干字符"中其他部分必须写成[^>]*,否则它无法匹配名字为单个字符的标签,比如<b>。

close tag的特点是以<开头,之后是/字符,然后是"若干字符(但不能以/开头)",最后是>,所以对应的正则表达式是</[^>]+>;

self-closing tag的特点是以<开头,中间是"若干字符",最后是/>,所以对应的正则表达式是<[^>]+/>。注意:这里不是<[^>/]+/>,排除型字符组只排除>,而不排除/,因为要确认的只是在结尾的>之前出现/,如果写成<[^>/]+/>,则要求tag内部不能出现/,就无法匹配<img src="http://somehost/picture" />这类的tag了。

表2-3列出了匹配几类tag的表达式。

表2-3  各类tag的匹配

匹配所有tag的表达式

tag分类

匹配分类tag的表达式

<[^>]+>

open tag

<[^/>][^>]*>

close tag

</[^>]+>

self-closing tag

<[^>/]+/>

对比表格中"匹配所有tag的表达式"和"匹配分类tag的表达式",可以发现它们的模式是相近的,只是细节上有差异。也就是说,通过变换字符组和量词,可以准确控制正则表达式能匹配的字符串的范围,达到不同的目的。这其实是使用正则表达式时的一条根本规律:使用合适的结构(包括字符组和量词),精确表达自己的意图,界定能匹配的文本。

再仔细观察,你或许会发现,匹配open tag的表达式,也可以匹配self-closing tag:<[^/][^>]*>能够匹配<br/>,因为[^>]*并不排除对/的匹配。那么将表达式改为<[^/][^>]*[^/]>,就保证匹配的open tag不会以/>结尾了。

不过这会产生新的问题:<[^/][^>]*[^/]>能匹配的tag,在<和>之间出现了两个[^/],上一章已经讲过,排除型字符组表示"在当前位置,匹配一个没有列出的字符",所以tag里的字符串必须至少包含两个字符,这样就无法匹配<u>了。

仔细想想,真正要表达的意思是,在tag内部的字符串不能以/开头,也不能以/结尾,如果这个字符串只包含一个字符,那么它既是开头,又是结尾,使用两个排除型字符组显然是不合适的,看起来没办法解决了。实际上,只是现有的知识还不足够解决这个问题而已,在第68页有这个问题的详细解法。

posted @ 2018-10-19 15:07  gaara724  阅读(775)  评论(0编辑  收藏  举报