Python3 如何优雅地使用正则表达式(详解一)

注:本文翻译自 Regular Expression HOWTO,小甲鱼童鞋对此作了一些注释和修改。


正则表达式介绍

正则表达式(Regular expressions 也称为 REs,或 regexes 或 regex patterns)本质上是一个微小的且高度专业化的编程语言。它被嵌入到 Python 中,并经过 re 模块提供给程序猿使用。使用正则表达式,你须要指定一些规则来描述那些你但愿匹配的字符串集合。这些字符串集合可能包含英语句子、 e-mail 地址、TeX 命令,或任何你想要的东东。

正则表达式模式被编译成一系列的字节码,而后由一个 C 语言写的匹配引擎所执行。对于高级的使用,你可能须要更关注匹配引擎是如何执行给定的 RE,并经过必定的方式来编写 RE,以便产生一个能够运行得更快的字节码。本文暂不讲解优化的细节,由于这须要你对匹配引擎的内部机制有一个很好的理解。但本文的例子均是符合标准的正则表达式语法。

小甲鱼注释:Python 的正则表达式引擎是用 C 语言写的,因此效率是极高的。另,所谓的正则表达式,这里说的 RE,就是上文咱们提到的“一些规则”。

正则表达式语言相对较小,而且受到限制,因此不是全部可能的字符串处理任务均可以使用正则表达式来完成。还有一些特殊的任务,可使用正则表达式来完成,可是表达式会所以而变得很是复杂。在这种状况下,你可能经过本身编写 Python 代码来处理会更好些;尽管 Python 代码比一个精巧的正则表达式执行起来会慢一些,但可能会更容易理解。

小甲鱼注释:这多是你们常说的“丑话说在前”吧,你们别管他,正则表达式很是优秀,她能够处理你 98.3% 的文本任务,必定要好好学哦~~~~~


简单的模式

咱们将从最简单的正则表达式学习开始。因为正则表达式经常使用于操做字符串的,所以咱们从最多见的任务下手:字符匹配。


字符匹配

大多数字母和字符会匹配它们自身。举个例子,正则表达式 FishC 将彻底匹配字符串 "FishC"。(你能够启用不区分大小写模式,这将使得 FishC 能够匹配 "FISHC" 或 "fishc",咱们会在后边讨论这个话题。)

固然这个规则也有例外。有少数特殊的字符咱们称之为元字符(metacharacter),它们并不能匹配自身,它们定义了字符类、子组匹配和模式重复次数等。本文用很大的篇幅专门讨论了各类元字符及其做用。

下边是元字符的完整列表(咱们将在后边逐一讲解):

.   ^   $   *   +   ?   { }   [ ]   \   |   ( )

小甲鱼注释:若是没有这些元字符,正则表达式就变得跟字符串的 find() 方法同样平庸了......


咱们先来看下方括号 [ ],它们指定一个字符类用于存放你须要匹配的字符集合。能够单独列出须要匹配的字符,也能够经过两个字符和一个横杆 - 指定匹配的范围。例如 [abc] 会匹配字符 ab 或 c[a-c] 能够实现相同的功能。后者使用范围来表示与前者相同的字符集合。若是你想只匹配小写字母,你的 RE 能够写成 [a-z]

须要注意的一点是:元字符在方括号中不会触发“特殊功能”,在字符类中,它们只匹配自身。例如 [akm$] 会匹配任何字符 'a''k''m' 或 '$''$' 是一个元字符,但在方括号中它不表示特殊含义,它只匹配 '$' 字符自己。

你还能够匹配方括号中未列出的全部其余字符。作法是在类的开头添加一个脱字符号 ^ ,例如 [^5] 会匹配除了 '5' 以外的任何字符。


或许最重要的元字符当属反斜杠 \ 了。跟 Python 的字符串规则同样,若是在反斜杠后边紧跟着一个元字符,那么元字符的“特殊功能”也不会被触发。例如你须要匹配符号 [ 或 \,你能够在它们前面加上一个反斜杠,以消除它们的特殊功能:\[\\

反斜杠后边跟一些字符还能够表示特殊的意义,例如表示十进制数字,表示全部的字母或者表示非空白的字符集合。

小甲鱼解释:反斜杠真牛逼,反斜杠后边跟元字符去除特殊功能,反斜杠后边跟普通字符实现特殊功能。

让咱们来举个例子:\w 匹配任何字符。若是正则表达式以字节的形式表示,这至关于字符类 [a-zA-Z0-9_];若是正则表达式是一个字符串,\w 会匹配全部 Unicode 数据库(unicodedata 模块提供)中标记为字母的字符。你能够在编译正则表达式的时候,经过提供 re.ASCII 表示进一步限制 \w 的定义。

小甲鱼解释:re.ASCII 标志使得 \w 只能匹配 ASCII 字符,不要忘了,Python3 是 Unicode 的。

下边列举一些反斜杠加字符构成的特殊含义:

html

特殊字符 含义
\d 匹配任何十进制数字;至关于类 [0-9]
\D 与 \d 相反,匹配任何非十进制数字的字符;至关于类 [^0-9]
\s 匹配任何空白字符(包含空格、换行符、制表符等);至关于类 [ \t\n\r\f\v]
\S 与 \s 相反,匹配任何非空白字符;至关于类 [^ \t\n\r\f\v]
\w 匹配任何字符,见上方解释
\W 于 \w 相反
\b 匹配单词的开始或结束
\B 与 \b 相反


它们能够包含在一个字符类中,而且同样拥有特殊含义。例如 [\s,.] 是一个字符类,它将匹配任何空白字符(/s 的特殊含义),',' 或 '.'。 

最后咱们要讲的一个元字符是 .,它匹配除了换行符之外的任何字符。若是设置了 re.DOTALL 标志,. 将匹配包括换行符在内的任何字符。


重复的事情

使用正则表达式可以轻松的匹配不一样的字符集合,但 Python 字符串现有的方法却没法实现。然而,若是你认为这是正则表达式的惟一优点,那你就 too young too native 了。正则表达式有另外一个强大的功能,就是你能够指定 RE 部分被重复的次数。


咱们来看看 * 这个元字符,固然它不是匹配 '*' 字符自己(咱们说过元字符都是有特殊能力的),它用于指定前一个字符匹配零次或者屡次。

例如 ca*t 将匹配 ct(0 个字符 a),cat(1 个字符 a),caaat(3 个字符 a),等等。须要注意的是,因为受到 C 语言的 int 类型大小的内部限制,正则表达式引擎会限制字符 'a' 的重复个数不超过 20 亿个;不过,一般咱们工做中也用不到那么大的数据。


正则表达式默认的重复规则是贪婪的,当你重复匹配一个 RE 时,匹配引擎会尝试尽量多的去匹配。直到 RE 不匹配或者到告终尾,匹配引擎就会回退一个字符,而后再继续尝试匹配。

咱们经过例子一步步的给你们讲解什么叫“贪婪”:先考虑一下表达式 a[bcd]*b,首先须要匹配字符 'a',而后是零个到多个 [bcd],最后以 'b' 结尾。那如今想象一下,这个 RE 匹配字符串 abcbd 会怎样?

python

步骤 匹配 说明
1 a 匹配 RE 的第一个字符 'a'
2 abcbd 引擎在符合规则的状况下尽量地匹配 [bcd]*,直到该字符串的结尾
3 失败 引擎尝试匹配 RE 最后一个字符 'b',但当前位置已是字符串的结尾,因此失败了结
4 abcb 回退,因此 [bcd]* 匹配少一个字符
5 失败 再一次尝试匹配 RE 最后一个字符 'b',但字符串最后一个字符是 'd',因此失败了结
6 abc 再次回退,因此 [bcd]* 此次只匹配 'bc'
7 abcb 再一次尝试匹配字符 'b',这一次字符串当前位置指向的字符正好是 'b',匹配成功


最终,RE 匹配的结果是 abcb

小甲鱼解释:正则表达式默认的匹配规则是贪婪的,后边有教你如何使用非贪婪的方法匹配。


另外一个实现重复的元字符是 +,用于指定前一个字符匹配一次或者屡次。

要特别注意 * 和 + 的区别:* 匹配的是零次或者屡次,因此被重复的内容可能压根儿不会出现;+ 至少须要出现一次。例如 ca+t 会匹配 cat 和 caaat,但不会匹配 ct


还有两个表示重复的元字符,其中一个是问号 ?,用于指定前一个字符匹配零次或者一次。你能够这么想,它的做用就是把某种东西标志位可选的。例如 小?甲鱼 能够匹配 小甲鱼,也能够匹配 甲鱼


最灵活的应该是元字符 {m, n}(m 和 n 都是十进制整数),上边讲到的几个元字符均可以使用它来表达,它的含义是前一个字符必须匹配 m 次到 n 次之间。例如 a/{1, 3}b 会匹配 a/ba//b 和 a///b。但不会匹配 ab(没有斜杠);也不会匹配a////b(斜杠超过三个)。

你能够省略 m 或者 n,这样的话,引擎会假定一个合理的值代替。省略 m,将被解释为下限 0;省略 n 则会被解释为无穷大(事实上是上边咱们提到的 20 亿)。

小甲鱼解释:若是是 {, n} 至关于 {0, n};若是是 {m, } 至关于 {m, +无穷};若是是 {n},则是重复前一个字符 n 次。


聪明的鱼油应该已经发现了,其实 *+ 和 ? 均可以使用 {m, n} 来代替。{0,} 跟 * 是同样的;{1, } 跟 + 是同样的;{0, 1}跟 ? 是同样的。不过仍是鼓励你们记住并使用 *+ 和 ?,由于这些字符更短而且更容易阅读。

小甲鱼解释:还有一个缘由是匹配引擎对 * + ? 作了优化,效率要更高些。



正则表达式

相关文章
相关标签/搜索