本文译自官方文档:Regular Expression HOWTO
全文下载:Python正则表达式基础
=========================================================================================
摘要
本篇文档是在Python中通过re模块使用正则表达式的引导手册,提供了一个比参考类库相应部分更详细的介绍。
=========================================================================================
1.简介
正则表达式(也称为REs,regexes,或者regex patterns)本质上是一个被嵌入到Python中的一个精细的、高度专业化的程序语言,并通过re模块提供给程序员使用。使用正则表达式,你需要为那些你想要匹配的字符串集合定制一些规则;这些集合可能包含英文句子、邮箱地址、TeX命令或者任何你想要的东西。然后你就会问类似于这样的问题:“这个字符串匹配这个模式吗?”或者“在这个字符串中有适合的模式来匹配吗?”你可以使用正则表达式去修改一个字符串或者用各种各样的方式去分割字符串。
正则表达式模式被编译成一系列的字节码,然后由一个用C语言写的匹配引擎执行。对于高级的应用,你必须要注意对于一个给定的RE,引擎是如何执行的,并通过一定的方式来编写RE以便这些字节码运行地更快。但本篇文档不包含RE优化的内容,因为这些需要你对引擎内核有一个很好的理解。
正则表达式语言相对较小,也比较严格,所以并不是所有的字符串处理任务都可以用正则表达式完成。有些任务可以用正则表达式做,但是表达式非常复杂。在这些情况下,你最好通过常规Python代码完成任务,虽然Python代码可能会比精心编写的正则表达式运行速度要慢,但是相对来说,它更好理解。
=========================================================================================
2.简单的模式
我们从学习最简单的正则表达式开始。由于正则表达式常被用于处理字符串,所以我们由最常见的任务入手:字符匹配。
对于计算机科学中潜在的正则表达式的更多细节,你可以参考编写编译器的有关资料。
------------------------------------------------------------------------------------------------------------------------------------------------------------
2.1.字符匹配
大多数字符和字母会简单地匹配它们自己,比如,正则表达式test可以完全匹配字符串test(你可以使用不区分大小写模式,那么这个正则表达式也可以匹配Test或者TEST;稍后会介绍更多这方面的细节)。
当然这个规则也有例外,有一些字符是特殊的【元字符】,它们并不匹配它们自身。相反的,它们表示将要匹配一些不同的东西,或者通过重复、改变意义等影响RE中的其他部分。本篇文档大部分内容都致力于讨论各种元字符和它们的用法。
这是元字符的完整列表,它们的意义将在随后讨论:
. ^ $ * + ? { } [ ] \ | ( )
我们先看一下方括号 [ ] .它们指定一个【字符类】,用来存放你希望匹配的字符集合。这些字符可以单独列出,一个连续范围内的字符也可以用横杠‘-’连接首尾两个字符指定。比如,[abc]可以匹配a,b或c,[a-c]也有同样的效果,后者使用范围表达式来指定与前者相同的字符集合。如果你只想匹配小写字母,那么你的正则表达式为[a-z]。
需要注意的是,元字符在方括号内不会被激活!比如,[akm$]可以匹配字符‘a’,‘k’,‘m’或者‘$’,我们知道‘$’也是一个元字符,但是在方括号内,它就会失去它的特性,只会匹配它自身。
可能最重要的元字符是反斜杠\。在Python常规语法中,反斜杠后面跟上不同的字符表示特殊的序列。在正则表达式中也是一样,如果反斜杠后面跟着一个元字符,那么元字符的“特殊功能”将不会被触发。比如你需要去匹配一个 [ 或者 \ ,你可以在它们之前加上反斜杠 \ 以去掉它们的特殊语义:\ [ 或者 \\
------------------------------------------------------------------------------------------------------------------------------------------------------------
2.2.重复的事情
能够匹配不同的字符是正则表达式可以做的第一件事情,但Python字符串现有的方法却无法实现。然而,如果这是正则表达式唯一的优势,那么它也不会有太大的进步。它的另一个强大的功能是你可以指定RE部分被重复的次数。
首先我们来学习有重复作用的第一个元字符星号 * ,它并不匹配它自身,而是指定它的前一个字符重复0次或者多次,而不是确定的多少次。
比如,ca*t可以匹配ct(有0个a字符)、cat(有1个a字符)、caaat(有3 个a字符)等等。不过受到正则表达式引擎C语言中int类型的限制,‘a’字符的重复次数不能超过2亿次,然而我们平常也用不到这么大的数据。
正则表达式的重复规则是贪婪的,当重复匹配一个RE时,匹配引擎会尽可能多地去匹配,直到不匹配或者到了结尾,匹配引擎就会回退一个字符,然后再尝试匹配。
我们通过例子来一步步地让这个概念更清晰。让我们考虑一个正则表达式a[bcd]*b,它要先匹配字符‘a’,然后匹配0个或多个字符类[bcd]中的字符,最后以字符‘b’结尾。现在假如它要匹配字符串‘abcbd’:
步骤 | 匹配 | 解释 |
1 | a | 匹配RE的第一个字符a |
2 | abcbd | 引擎尽可能远地匹配[bcd]*,直到该字符串的结尾 |
3 | Failure | 引擎尝试去匹配b,但当前位置已经是字符串的末尾了,所以失败 |
4 | abcb | 回退一个字符,所以[bcd]*匹配少一个字符 |
5 | Failure | 引擎再次尝试匹配b,但是当前位置最后一个字符是d,所以失败 |
6 | abc | 再次回退一个字符,所以[bdc]*只匹配bc |
7 | abcb | 再次匹配b,这一次当前位置的字符正好是b,所以匹配成功 |
另一个表示重复的元字符是加号 + ,它指定匹配它前面的字符1次或者多次。这里要注意星号 * 和加号 + 的区别,星号 * 匹配0次或者多次,所以被匹配的内容可能压根儿就不出现,但是加号 + 要求重复的字符至少出现1次。举一个简单的例子,ca+t可以匹配cat(1个a字符)、caaat(3个a字符),但是它不匹配ct。
还有两个表示重复的元字符,其中一个就是问号 ? ,它用于指定匹配它前面的字符0次或者1次,你可以认为它用于标志一个可选的字符。比如,home-?brew可以匹配homebrew或者home-brew。
最灵活的表示重复的限定符应该是{m,n},这里的m和n都是十进制数字。这表示它前面的字符至少重复m次,最多重复n次。比如,a/{1,3}b可以匹配a/b,a//b和a///b。但是它不匹配ab,因为ab没有斜杠,也不匹配a////b,因为它有4个斜杠超过了3个。
你也可以省略m或者n,在这种情况下,引擎会假定一个合理的替代值。省略m将默认下限为0,省略n将默认无上限,即上限无穷大——当然,根据上文提到的,它不能超过2亿。(译者注:还有一种使用方式,{n}指定它前面的字符重复n次)
聪明的读者可能已经发现了,上文提到的星号 *、加号 +和问号 ?都可以用这个限定符表示。{0,}与星号 * 作用一样,{1,}与加号 + 作用一样,{0,1}与问号 ? 作用一样。然而,在实际应用中,首选星号 * 、加号 + 和问号 ?,因为它们更短,效率更高。