15.2 正则表达式使用的特殊符号和字符

现在,我们来介绍最常用的元字符(metacharacter)——特殊字符和符号,正是它们赋予了正则表达式强大的功能和灵活性。正则表达式中最常见的符号和字符见表15.1。

15.2 正则表达式使用的特殊符号和字符 - 图1

15.2 正则表达式使用的特殊符号和字符 - 图2

15.2.1 用管道符号(|)匹配多个正则表达式模式

管道符号(|),就是你键盘上的竖杠,表示一个或操作,它的意思是选择被管道符号分隔的多个不同的正则表达式中的一个。例如,下面的一些使用或操作的模式,和它们所匹配的字符串:

15.2 正则表达式使用的特殊符号和字符 - 图3

有了这个符号,正则表达式的灵活性增强了,使得它可以匹配不止一个字符串,“或”(操作)有时候也被叫做“联合”(union)或者逻辑或(OR)。

15.2.2 匹配任意一个单个的字符(.)

点字符或句点(.)符号匹配除换行符(NEWLINE)外的任意一个单个字符(Python的正则表达式有一个编译标识[S or DOTALL],该标识能去掉这一限制,使(.)在匹配时包括换行符(NEWLINE))。无论是字母、数字、不包括“\n”的空白符、可打印的字符、还是非打印字符,或是一个符号、点(.)都可以匹配他们。

15.2 正则表达式使用的特殊符号和字符 - 图4

问:我怎样才能匹配句点(dot)或句号(period)?

答:为了明确地匹配一个句点(dot)本身,你必须(在前面)使用反斜线“\”,对它进行转义。

15.2.3 从字符串的开头或结尾或单词边界开始匹配(^/$ ΛbΛB )

还有些符号和特殊字符是用来从字符串的开头或结尾开始搜索正则表达式模式的。如果想从字符串的开头开始匹配一个模式,你必须用脱字符号(^,即,Caret)或特殊字符\A(大写字母A前面加上一个反斜线)。后者主要是为那些没有caret符号的键盘使用的,比如说国际键盘。类似,美元符号($)或\Z是用来(零宽度)匹配字符串的结尾的。

用这些符号的模式与我们将在本章讲述的其他大多数符号是不同的,因为这些符号指定了(匹配字符)的位置。在上面的核心笔记里,我们曾说过“匹配”和“搜索”之间的区别,“匹配”是试图从整个字符串的开头进行匹配,而“搜索”则可从一个字符串的任意位置开始匹配。正因为这几个字符和搜索的位置有关,所以需要和搜索模式一起使用。下面是几个“擦边球”的正则表达式搜索模式:

15.2 正则表达式使用的特殊符号和字符 - 图5

特别说明,如果你想匹配这两个字符中的任何一个(或全部),就必须用反斜线进行转义。例如,如果你想匹配任何以美元符号($)结尾的字符串,一个可行的解决办法是用正则表达式模式“.*$$”。

特殊字符\b and \B用来匹配单词边界。两者之间的区别是,\b匹配的模式是一个单词边界,就是说,与之对应的模式一定在一个单词的开头,不论这个单词的前面是有字符(该词在一个字符串的中间),还是没有字符(该单词在一行的起始处)。同样地,\B只匹配出现在一个单词中间的模式(即,不在单词边界上的字符)。看下面几个例子:

15.2 正则表达式使用的特殊符号和字符 - 图6

15.2.4 创建字符类([])

尽管句点可用来匹配任意字符,但有时候你需要匹配某些个特殊的字符。正因为如此,方括号([])被发明出来。使用方括号的正则表达式会匹配方括号里的任何一个字符。几个例子如下:

15.2 正则表达式使用的特殊符号和字符 - 图7

关于正则表达式“[cr][23][dp][o2]”的一点要说明:如果只让“r2d2或“c3po”成为有效的字符串,就需要限定更为严格的正则表达式。但因为方括号只有“逻辑或”(“logical OR”)的功能,所以用方括号不能实现这一限定要求。唯一的解决办法是用管道符号(pipe),例如:“r2d2|c3po”。

对仅有单个字符的正则表达式,使用管道符号和方括号的效果是等价的。举例来说,正则表达式“ab”,只匹配以“a”开头后面再跟一个“b”的字符串。如果我们只想要一个字母的字符串,即, “a”或者“b”中的一个,就可以使用正则表达式“[ab]”。因为“a”和“b”是单个的字符串,我们也可以用正则表达式“a|b”。但是,如果我们想用模式匹配“ab”,后面接着是“cd”的字符串,就不能用方括号了,因为方括号只适用于单个字符的情况。这样,唯一的办法是用“ab|cd”,这和我们刚才提到的“r2d2|c3po”的道理是相同的。

15.2.5 指定范围(-)和否定(^)

方括号除匹配单个字符外,还可以支持所指定的字符范围。方括号里一对符号中间的连字符(-)用来表示一个字符的范围,例如A-Z、a-z或0-9分别代表大写字母、小写字母和十进制数字。这是一个按字母顺序排序的范围,所以它不限于只用在字母和十进制数字上。另外,如果在左方括号后第一个字符是上箭头符号(^),就表示不匹配指定字符集里的任意字符。

15.2 正则表达式使用的特殊符号和字符 - 图8

15.2 正则表达式使用的特殊符号和字符 - 图9

15.2.6 使用闭包操作符(*,+,?,{})实现多次出现/重复匹配

现在我们来介绍最常用的正则表达式符号,即,特殊符号“*”、“+”和“?”,它们可以用于匹配字符串模式出现一次、多次或未出现的情况。星号或称星号操作符匹配它左边那个正则表达式出现零次或零次以上的情况(在计算机语言和编译器原理里,此操作符被叫做Kleene闭包操作符)。加号(+)操作符匹配它左边那个正则表达式模式至少出现一次的情况(它也被称为正闭包操作符),而问号操作符(?)匹配它左边那个正则表达式模式出现零次或一次的情况。

还有花括号操作符({}),花括号里可以是单个的值,也可以是由逗号分开的一对值。如果是一个值,如,{N},则表示匹配N次出现;如果是一对值,即,{M, N},就表示匹配M次到N次出现。可以在这些符号前用反斜线进行转义,使它们失去特殊作用,即,“*ast;”将匹配星号本身等。

在上表中,我们注意到问号出现了不只一次(被重载),问号有两种含义:1.单独使用时表示匹配出现零次或一次的情况,2.紧跟在表示重复的元字符后面时,表示要求搜索引擎匹配的字符串越短越好,例如(+?)。

前面提到“越短越好”是什么意思呢?当使用了表示重复的元字符(*+? {m,n})时,正则表达式引擎在匹配模式时会尽量“吸收”更多的字符。这就叫做”贪心”。问号告诉正则表达式引擎尽可能地偷懒,要求当前匹配消耗的字符越少越好,留下尽可能多的字符给后面的模式(如果存在)。在本章末尾,我们举一个有代表性的例子来说明必须使用非贪心模式的情况。

现在,让我们接着来看一些使用闭包操作符的例子。

15.2 正则表达式使用的特殊符号和字符 - 图10

15.2.7 特殊字符表示、字符集

我们还提到有一些特殊字符可以用来代表字符集合。例如,你可以不使用“0-9”这个范围表示十进制数字,而改用简写“\d”表示。另一个特殊的字符“\w”可用来表示整个字符数字的字符集,即相当于“A-Za-z0-9_”的简写形式,特殊字符“\s”代表空白字符。这些特殊字符的大写形式表示不匹配,比如,“\D”表示非十进制数字的字符(等价于“0-9”),等等。我们来看几个运用这些简写形式的稍复杂的例子。

15.2 正则表达式使用的特殊符号和字符 - 图11

15.2.8 用圆括号(())组建组

现在,或许我们可以匹配一个字符串和丢弃那些不匹配的字符串了,但有时候,我们也许对匹配的数据本身更有兴趣。我们不仅想知道是否整个字符串匹配我们的条件(正则表达式),还想在匹配成功时取出某个特定的字符串或子字符串。要达到这个目的,只需要给正则表达式的两边加上一对圆括号。

一对圆括号(())和正则表达式一起使用时可以实现以下任意一个(或两个)功能:

  • 对正则表达式进行分组

  • 匹配子组

有时你需要对正则表达式进行分组,其中一个很好的例子就是,你要用两个不同的正则表达式去比较一个字符串。另一个理由是为整个正则表达式添加一个重复操作符(即不是仅重复单个字符或单一字符集)。

使用圆括号的一个额外好处就是匹配的子串会被保存到一个子组,便于今后使用。这些子组可以在同一次匹配或搜索中被重复调用,或被提取出来做进一步处理。在15.3.9小节的结尾你会读到一些提取子组的例子。

为什么需要使用子组匹配呢?主要是有时除了进行匹配操作外,你还想要提取匹配模式的内容。如果想知道在成功的匹配中,是哪些字符串匹配了我们的正则表达式模式。例如,我们想用正则表达式 “\w+-\d+”匹配一些内容,但又想把第一部分的字符和第二部分的数字分别保存,该怎么做呢?

如果我们给两个子模式都加上圆括号,即,将它写成“(\w+)- (\d+)”,那我们就可以对这两个匹配的子组分别进行访问了。当然你也可以使用其他方法达到同样目的,比如,先写一段代码判断是否找到匹配的对象,然后再执行另一个程式(也必须再写一段代码)来解析整个匹配的部分,从中提取出两个部分来。然而相比之下把正则表达式划分为子组是更好的实现办法,因为Python已经在re模块里支持此功能,那为什么不让Python来做这项工作,而非要重复发明一个轮子呢?

15.2 正则表达式使用的特殊符号和字符 - 图12