Python正则表达式操作指南(转帖)
上一篇 / 下一篇 2009-08-27 22:50:53 / 个人分类:Python
原文作者:A.M. Kuchling (amk@amk.ca)
t!dZYJ.CG;H0授权许可:创作共用协议51Testing软件测试网@ Xn'A;Z3D
翻译人员:FireHare
pdZfy2j9?H!I0校对人员:Leal
{F-Y)RXU?0适用版本:Python 1.5 及后续版本51Testing软件测试网L/Exv)Lfz%Q(bIj
简介
o)U f }o7e/m1wz0!Ci8I2s!`\"K0Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。Python 1.5之前版本则是通过 regex 模块提供 Emecs 风格的模式。Emacs 风格模式可读性稍差些,而且功能也不强,因此编写新代码时尽量不要再使用 regex 模块,当然偶尔你还是可能在老代码里发现其踪影。
}'Oy:s*a.xZ-lf$TN07^GY*RC[)l#W+bI-HH0就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。使用这个小型语言,你可以为想要匹配的相应字符串集指定规则;该字符串集可能包含英文语句、e-mail地址、TeX命令或任何你想搞定的东西。然后你可以问诸如“这个字符串匹配该模式吗?”或“在这个字符串中是否有部分匹配该模式呢?”。你也可以使用 RE 以各种方式来修改或分割字符串。51Testing软件测试网1~xkKo6R;AF
5U$N;^Tt'^sy0正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。在高级用法中,也许还要仔细留意引擎是如何执行给定 RE ,如何以特定方式编写 RE 以令生产的字节码运行速度更快。本文并不涉及优化,因为那要求你已充分掌握了匹配引擎的内部机制。
VVN"AR3P+a,^S6j}*U0q$W%U2v:}#_0正则表达式语言相对小型和受限(功能有限),因此并非所有字符串处理都能用正则表达式完成。当然也有些任务可以用正则表达式完成,不过最终表达式会变得异常复杂。碰到这些情形时,编写 Python 代码进行处理可能反而更好;尽管 Python 代码比一个精巧的正则表达式要慢些,但它更易理解。51Testing软件测试网3Od-I0M {2g7]u
m&U,VVfOD_0简单模式51Testing软件测试网Lt"AAO8X/r[
&~9S9]3uHJ'P0我们将从最简单的正则表达式学习开始。由于正则表达式常用于字符串操作,那我们就从最常见的任务:字符匹配 下手。51Testing软件测试网xDY*e B4Es1ut,Cu
51Testing软件测试网-i\Pa0H/L%M`z有关正则表达式底层的计算机科学上的详细解释(确定性和非确定性有限自动机),你可以查阅编写编译器相关的任何教科书。51Testing软件测试网#irmu:Z1t"\%[
51Testing软件测试网Y [ e YT-U fd1. 字符匹配
n$y|4a$D^:C6@&PQ051Testing软件测试网'QH H.{7q^*@i(d8E大多数字母和字符一般都会和自身匹配。例如,正则表达式 test 会和字符串“test”完全匹配。(你也可以使用大小写不敏感模式,它还能让这个 RE 匹配“Test”或“TEST”;稍后会有更多解释。)51Testing软件测试网1f [)?Uvl~
51Testing软件测试网)R|F%l.tp这个规则当然会有例外;有些字符比较特殊,它们和自身并不匹配,而是会表明应和一些特殊的东西匹配,或者它们会影响到 RE 其它部分的重复次数。本文很大篇幅专门讨论了各种元字符及其作用。
G&hY SsEpm051Testing软件测试网5F}|!sGxa这里有一个元字符的完整列表;其含义会在本指南余下部分进行讨论。51Testing软件测试网~)DQ%Tn9B*p&c*m.P u
@'L2i/q3f0. ^ $ * + ? { [ ] \ | ( )
0~7A#a2MQ zm5YC0i0F#vC{J+l`K0我们首先考察的元字符是 "[" 和 "]"。它们常用来指定一个字符类别,所谓字符类别就是你想匹配的一个字符集。字符可以单个列出,也可以用“-”号分隔的两个给定字符来表示一个字符区间。例如, [abc] 将匹配"a", "b", 或 "c"中的任意一个字符;也可以用区间[a-c]来表示同一字符集,和前者效果一致。如果你只想匹配小写字母,那么 RE 应写成 [a-z]。
2Npqf \:h u051Testing软件测试网6X:j0l+w*TW2@8tV]元字符在类别里并不起作用。例如,[akm$]将匹配字符"a", "k", "m", 或 "$" 中的任意一个;"$"通常用作元字符,但在字符类别里,其特性被除去,恢复成普通字符。51Testing软件测试网hn/a9V U Iv
51Testing软件测试网 |Lch)Q0C1G你可以用补集来匹配不在区间范围内的字符。其做法是把"^"作为类别的首个字符;其它地方的"^"只会简单匹配 "^" 字符本身。例如,[^5] 将匹配除 "5" 之外的任意字符。
G]-z }&X6R.Z*Gz Lq0i/P*MFG"lG'^0也许最重要的元字符是反斜杠"\"。 做为 Python 中的字符串字母,反斜杠后面可以加不同的字符以表示不同特殊意义。它也可以用于取消所有的元字符,这样你就可以在模式中匹配它们了。举个例子,如果你需要匹配字符 "[" 或 "\",你可以在它们之前用反斜杠来取消它们的特殊意义: \[ 或 \\。
qT/A R9n4Gp0/Xg'J|%B c0d0一些用 "\" 开始的特殊字符所表示的预定义字符集通常是很有用的,象数字集,字母集,或其它非空字符集。下列是可用的预设特殊字符:
:m't4r/^\ R)K|%mg [0WHBP5\\0\d51Testing软件测试网sM%ZR*L oniB{;^
51Testing软件测试网4pG.^[p/E* 匹配任何十进制数;它相当于类 [0-9]。
A'e |$r!YT$q051Testing软件测试网3SQYa%H{4qTROa/x\D
Uh%Pm&\051Testing软件测试网 G.z#\U fm'B7o* 匹配任何非数字字符;它相当于类 [^0-9]。51Testing软件测试网Ym vAUj I
51Testing软件测试网*TH-|ZH/e@T{&l(v\s
9u\2S ^zgH07?6w7n*| xH0* 匹配任何空白字符;它相当于类 [ \t\n\r\f\v]。51Testing软件测试网RZj8lJ^!SNQ
51Testing软件测试网[a-[({7b(w I;C\S51Testing软件测试网4A$}6@"gO(`@
51Testing软件测试网&zx"ga l/E;j/k* 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。
4m%V i'~M[DMd Dq0XSqg7@ V4a"ac0\w
N4fn4y7Ml W3U9r0`'e4^N$}^*P(c0* 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
8K$} r4{2\*R!k(V[ v051Testing软件测试网-sZN5R]3Q/l?'C6KC0m\W51Testing软件测试网U/am&rF$F
*K8xu FBz.y0* 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]。51Testing软件测试网QR7`an&hR_F3yx
51Testing软件测试网TB%cJ+k.Ce这样特殊字符都可以包含在一个字符类中。如,[\s,.]字符类将匹配任何空白字符或","或"."。
(VK$J~Nb Qe/?051Testing软件测试网GvI:Ik M9@4L本节最后一个元字符是 . 。它匹配除了换行字符外的任何字符,在 alternate 模式(re.DOTALL)下它甚至可以匹配换行。"." 通常被用于你想匹配“任何字符”的地方。
f9O~ nSb1T'z051Testing软件测试网O&U(z8?TGm2. 重复51Testing软件测试网1z p` n$Mu4ox
51Testing软件测试网3reRQ/Ch"w/d2X0ID正则表达式第一件能做的事是能够匹配不定长的字符集,而这是其它能作用在字符串上的方法所不能做到的。 不过,如果那是正则表达式唯一的附加功能的话,那么它们也就不那么优秀了。它们的另一个功能就是你可以指定正则表达式的一部分的重复次数。
3C!C5X FVc"G051Testing软件测试网fz;`$U6_W i @m我们讨论的第一个重复功能的元字符是 *。* 并不匹配字母字符 "*";相反,它指定前一个字符可以被匹配零次或更多次,而不是只有一次。
*]^xW h&Yn"p051Testing软件测试网aIEy nn%ct举个例子,ca*t 将匹配 "ct" (0 个 "a" 字符), "cat" (1 个 "a"), "caaat" (3 个 "a" 字符)等等。RE 引擎有各种来自 C 的整数类型大小的内部限制,以防止它匹配超过2亿个 "a" 字符;你也许没有足够的内存去建造那么大的字符串,所以将不会累计到那个限制。
+y&]ds)Z1P&c f?a06y^,D$KRBf(\|if` E0象 * 这样地重复是“贪婪的”;当重复一个 RE 时,匹配引擎会试着重复尽可能多的次数。如果模式的后面部分没有被匹配,匹配引擎将退回并再次尝试更小的重复。51Testing软件测试网 {9nz2Gp#p,Y `
1Qzp'y R;sb0一步步的示例可以使它更加清晰。让我们考虑表达式 a[bcd]*b。它匹配字母 "a",零个或更多个来自类 [bcd]中的字母,最后以 "b" 结尾。现在想一想该 RE 对字符串 "abcbd" 的匹配。
)F(^,G-VN#P-?0Step Matched Explanation ------------------------------------- 1 a 匹配模式 2 abcbd 引擎匹配 [bcd]*,并尽其所能匹配到字符串的结尾 3 Failure 引擎尝试匹配 b,但当前位置已经是字符的最后了,所以失败 4 abcb 退回,[bcd]*尝试少匹配一个字符。 5 Failure 再次尝次b,但在当前最后一位字符是"d"。 6 abc 再次退回,[bcd]*只匹配 "bc"。 7 abcb 再次尝试 b ,这次当前位上的字符正好是 "b"51Testing软件测试网u&yx b:W?w
RE 的结尾部分现在可以到达了,它匹配 "abcb"。这证明了匹配引擎一开始会尽其所能进行匹配,如果没有匹配然后就逐步退回并反复尝试 RE 剩下来的部分。直到它退回尝试匹配 [bcd] 到零次为止,如果随后还是失败,那么引擎就会认为该字符串根本无法匹配 RE 。
L%eYr`:ze0[4q051Testing软件测试网nM3si-r O.f另一个重复元字符是 +,表示匹配一或更多次。请注意 * 和 + 之间的不同;* 匹配零或更多次,所以根本就可以不出现,而 + 则要求至少出现一次。用同一个例子,ca+t 就可以匹配 "cat" (1 个 "a"), "caaat" (3 个 "a"), 但不能匹配 "ct"。51Testing软件测试网G"SHt#S q0K6cx
51Testing软件测试网 Xha'T)jb e/S b1Y还有更多的限定符。问号 ? 匹配一次或零次;你可以认为它用于标识某事物是可选的。例如:home-?brew 匹配 "homebrew" 或 "home-brew"。
c"m8O-L5}A3Y.il0A5D;^&Cw0最复杂的重复限定符是 {m,n},其中 m 和 n 是十进制整数。该限定符的意思是至少有 m 个重复,至多到 n 个重复。举个例子,a/{1,3}b 将匹配 "a/b","a//b" 和 "a///b"。它不能匹配 "ab" 因为没有斜杠,也不能匹配 "a////b" ,因为有四个。51Testing软件测试网y$RPEn?T
8Z{2|k&U+l0你可以忽略 m 或 n;因为会为缺失的值假设一个合理的值。忽略 m 会认为下边界是 0,而忽略 n 的结果将是上边界为无穷大 -- 实际上是先前我们提到的 2 兆,但这也许同无穷大一样。
W-L!o I-A(cdDh0.u3K7?L4E2\0h,DZ0细心的读者也许注意到其他三个限定符都可以用这样方式来表示。 {0,} 等同于 *,{1,} 等同于 +,而{0,1}则与 ? 相同。如果可以的话,最好使用 *,+,或?。很简单因为它们更短也再容易懂。51Testing软件测试网V-qg*Z:I*[!aw&`*{"G
Q.W A @(k/w0使用正则表达式51Testing软件测试网aI]*I"E }-\)?/t,Ou
y&_2t!Y'J!?0?+S6f0现在我们已经看了一些简单的正则表达式,那么我们实际在 Python 中是如何使用它们的呢? re 模块提供了一个正则表达式引擎的接口,可以让你将 REs 编译成对象并用它们来进行匹配。51Testing软件测试网2t|9^t y}0|4AK
!anj^fZmUj01. 编译正则表达式
J"k,R(J9iT@051Testing软件测试网r5d|Um c:r&RNp C正则表达式被编译成 RegexObject 实例,可以为不同的操作提供方法,如模式匹配搜索或字符串替换。51Testing软件测试网Cz-L3A#iQRZ#i
切换行号显示
X;Zxl+`6Y9~01 <<< import re51Testing软件测试网@]*C:a*l/x3C8m
2 <<< p = re.compile('ab*')
)r:g QdD ]4FY03 <<< print p51Testing软件测试网#_!V8HpJQdkG2t
4 <re.RegexObject instance at 80b4150<
u;c*l)t}J+vK0re.compile() 也接受可选的标志参数,常用来实现不同的特殊功能和语法变更。我们稍后将查看所有可用的设置,但现在只举一个例子:51Testing软件测试网@f3z%@+d^[X3e;?
切换行号显示
1 <<< p = re.compile('ab*', re.IGNORECASE)
E2@mjIT&|0y051Testing软件测试网8ZT e"i[RE 被做为一个字符串发送给 re.compile()。REs 被处理成字符串是因为正则表达式不是 Python 语言的核心部分,也没有为它创建特定的语法。(应用程序根本就不需要 REs,因此没必要包含它们去使语言说明变得臃肿不堪。)而 re 模块则只是以一个 C 扩展模块的形式来被 Python 包含,就象 socket 或 zlib 模块一样。51Testing软件测试网:}0r(j)a].f
51Testing软件测试网DZ7ms]Q$m_k/N g将 REs 作为字符串以保证 Python 语言的简洁,但这样带来的一个麻烦就是象下节标题所讲的。51Testing软件测试网X8b,Vpw)|1QZ
.Zs#f/z~/h(? R02. 反斜杠的麻烦
o&M