分类: Python/Ruby
2010-03-23 13:52:32
学习正则表达式,最好的教材是《精通正则表达式》,而要精通NFA正则表达式,使用了NFA引擎的python正则模块官方文档就是最好的教材,大部分的功能同样在其他使用传统NFA引擎的正则包里受到支持。
目前公开引擎流派的:
使用DFA引擎的程序主要有:awk,egrep,flex,lex,MySQL,Procmail等;
使用传统型NFA引擎的程序主要有:GNU Emacs,Java,ergp,less,more,.NET语言,PCRE library,Perl,PHP,Python,Ruby,sed,vi;
使用POSIX NFA引擎的程序主要有:mawk,Mortice Kern Systems’ utilities,GNU Emacs(使用时可以明确指定);
使用DFA/NFA混合的引擎:GNU awk,GNU grep/egrep,Tcl。
正则表达式(RE)模块的功能主要是设置一个字符串并搜索其中的字串;这个模块的功能让你检查一个特定的字符串,匹配给定的正则表达式(或正则表达式匹配特定的字符串,它可以归结为对同样的事情,一般情况下,NFA引擎是由表达式主导,而python的re正是NFA引擎,从引擎的机制上来描述,应该是理解为拿正则表达式匹配特定的字符串;DNF引擎反之,详细可参考精通正则表达式一书关于正则引擎的章节)。
正则表达式可以被连接在一起,形成新的正则表达式,如果A和B都是正则表达式,那么AB同时也是正则表达式。一般来说,如果一个串p匹配了A,另外一个串q匹配了B,那么串pq会匹配AB。这种情况不包含A或B使用了低优先级描述(非贪婪匹配模式),命名捕获组(反向引用)。因此,复杂的表达式可以很容易地从简单易建的子表达式来描述的。对于理论和正则表达式的执行细节,请参考本书的引用,或几乎任何编译器构造的文档。
这里包含关于正则表达式的进一步信息和详细报告、使用指南:
正则表达式可以同时包含元字符(直译为特殊字符,国内一般称为元字符)和普通字符。大多数普通字符,如“a”,“1”或“0”,是最简单的正则表达式,它们只是描述本身。您可以连接普通的字符,因此last匹配字符串'last'。 (在本节的其余部分,我们将按照这种格式来描述,表达式通常不带引号,和包含在单引号的字符串进行匹配。)
一些字符,如“|”或“(”,是特殊的字符。特殊符号会对普通字符进行分组或者影响临近的普通字符、表达式。
包括以下元字符:
.
(Dot)在默认模式下,它匹配除了换行符号以外的任意字符。如果指定了DOTALL,那它将可匹配换行符。
^
(Caret)匹配字符串的开始,即物理行的开始,在多行模式下,也可匹配换行符后的位置,即逻辑行的开始。
$
匹配字符串的结尾或在字符串尾部的换行符之前的位置,在多行模式下,还可以匹配到一个新的行之前的位置。foo可以匹配'foo'和'foobar',而foo$只匹配'foo'。更有趣的是,在'foo1\nfoo2\n'中搜索foo.$,默认模式下匹配到'foo2',而在多行模式下,可匹配到'foo1'和'foo2'。不指定多行模式,$匹配物理行的结尾,而foo1为串的逻辑行之前的字符,所以未被匹配。
*
描述前边邻近的字符或子表达式,匹配0个或多个重复,因为许多重复是有可能的。ab*将匹配'a','ab',或者'a'后面跟大量的'b'。
+
描述前边邻近的字符或子表达式,匹配1个或多个重复。ab+将匹配'a'和后面跟着不少于1个的b,但不单独匹配'a'。
?
描述前边邻近的字符或子表达式,匹配0个或1个重复。ab?将匹配'a'或'ab'。
*?、+?、??
使用"*"、"+"、"?"这些量词描述符都是贪婪匹配的,它们会匹配尽可能多的字符。有时,这种行为并非是我们想要的,若使用<.*>去匹配'title
',它将会匹配整个字符串,而不只是''
。在量词描述符后面添加"?"后,会使匹配过程里,变为非贪婪模式,匹配尽可能少的字符。使用<.*?>去匹配之前的字符串,将只匹配。
{m}
描述前边邻近的字符或子表达式,尝试匹配尽可能多的m个重复。例如,a{6}匹配6个'a',不是5个。
{m,n}
描述前边邻近的字符或子表达式,尝试匹配尽可能多从m个到n个重复。例如a{3,5}将匹配3至5个'a',省略m,默认为0,即是匹配下限为0个,省略n则是匹配上限为无限。例如,a{4,}将匹配'aaaab'里的4个a或其中包含成千上万个'a',但最少应该有4个'a',并不匹配'aaab',逗号不可省略,以免与之前介绍的描述符冲突。
{m,n}?
描述前边邻近的字符或子表达式,尝试匹配尽可能少从m个到n个重复。这是非贪婪版本的{m,n}。例如,在6个字符的字符串'aaaaaa',a{3,5}将匹配5个'a',而a{3,5}?将匹配3个'a'。
\
用于转义元字符,或者简写标记的声明(将在下边谈论)。如果在python里你不适用raw原始字符串来作为表达式,请记住,表达式还需要为每个反斜杆前边在用一个反斜杆来转义。若转义序列并非由python作解析,那么\将会直接修饰之后的字符。但是如果由python来解释表达式,反斜杆应该重复2次。这比较复杂,不容易理解,所以强烈建议使用最简单的raw格式的原始字符串来写表达式。例如,r"aaa\(a\)"匹配'aaa(a)',假如使用普通字符串来编写表达式:"aaa\\(a\\)"。
[]
用于描述一个字符组(台翻译为字符集)。字符组可以是由多个单独字符组成,也可以使用连字符-来表示字符范围。特殊字符在字符组里是无效的,只作为普通字符描述。例如,[akm$]是匹配'a'、'k'、'm'、'$'里的其中一个字符;[a-z]将匹配所有小写字母,[a-zA-Z0-9]将匹配所有字母和数字;简写标记也可以在字符组里描述。如果你想包含']'或者'-',那就在该字符前使用反斜杆转义,或者将它写在字符组的第一个,例如[]]匹配']',注意,假如同时需要匹配'-'和']',就只能把其中一个放在第一位,另一个用反斜杆转义,推荐对所有不需要的元字符都使用反斜杆转义。您还可以匹配一个除了字符组里描述字符以外的字符,以脱字符'^'作为第一个字符,将标示这是一个排除型字符组,^在别的位置,只会匹配普通的'^'字符。例如,[^5]匹配除了5以外的字符;[^^]匹配除了脱字符以外的字符。
|
A|B,其中A和B可以是任意的子表达式,该表达式将可匹配A或者B。表达式里可以使用任意数量的|进行多选分支的分隔。这可用于分组捕获等(见下文)。对目标字符串进行扫描时,|是从左到右的顺序进行分隔。当一个分支被完全匹配,那么该分支就会被接受,这以为着,一旦A匹配,B就永远不会被尝试匹配。即使B会匹配更长的字符串。换句话说,在|操作符里,是永远不会贪婪匹配的。要匹配一个普通字符|,可使用\|转义写法,或者用字符组包含:[|]。
(...)
正则表达式会匹配括号内的字符,从开始字符到结束字符分为一个组,该组在捕获完成之后,可使用\数字来进行反向引用。需要匹配字符'('、')'时,使用转义符进行转义'\('、'\)',或者用字符组:[(]、[)]。
(?...)
捕获组中可使用扩展功能符,在(之后使用?,在?之后描述进一步的语法含义。扩展通常不会进行新的捕获,(?P
以下是目前支持的扩展功能:
(?iLmsux)
(可以一个或者多个修饰符同时使用:'i','L','m','s','u','x')
该组匹配空字符串;在模块中,相应的设置标志:re.I,re.L,re.M,re.S,re.U,re.X。如果你需要在表达式中指定匹配标志,而不是通过编译函数来指定,这是非常有用的,编译标志将影响整个表达式。请注意,(?x)的标志改变表达式的书写规则,如果在该标志之后包含了空白字符,那空白字符将会被忽略。例如aa(?i)a可匹配'AaA'。
(?:)
捕获组的无捕获版本。正则表达式会捕获任何在括号内的内容,但该捕获组不会捕获任何内容,也不可用于反向引用。一般用于做子表达式的界定符,例如给aa|b加限定:a(?:a|b)匹配'a'之后还有'a'或者'b',而不是原本的匹配'aa'或者匹配b。
(?P
功能类似捕获组,但该组匹配后可通过一个名称来引用捕获内容。分组名必须是有效的python标识符,每个组名在一个表达式只能被用一次。一个没有声明组名的捕获组其实也是一个id编号组。所以在上面的例子里,也可以把组命名并且进行反向引用。例如,表达式是:(?P
示例:
>>> m = re.match('a(?P
>>> m.group() #获取所有成功匹配内容
'abcde'
>>> m.group('id') #获取id组捕获内容
'b'
>>> m.end('id') #获取id组成功匹配结束位置点
2
>>> m.end() #获取整个表达式成功匹配结束位置点
5
>>> m = re.match('a(?P
>>> m.group()
'abcdeb'
>>> re.sub('a(?P
'abbh'
(?P=name)
反向引用,匹配之前对应的命名组所捕获的内容。
(?#...)
注释,包含在该括号内的内容将被忽略。
(?=...)
如果当前位置可匹配...,那么匹配接下来的表达式,但这个匹配只是一个判断,它并不消耗、占有任何字符,这被称为后向断言(顺序肯定环视)。例如Isaac (?=Asimov)可以匹配Isaac,但它后边必须跟着'Asimov'。
(?!...)
如果当前位置不可匹配...,那么匹配接下来的表达式。这是后向否定断言(顺序否定环视),例如,Isaac (?!Asimov) 会匹配 'Isaac ' ,当它后边不是跟着 'Asimov'的时候。
(?<=...)
如果当前位置点的前面符合某些条件,那么从这个点开始匹配接下来的表达式。这被称为前向断言(逆序肯定环视)。(?<=abc)会找到一个匹配在字符串'abcdef'中,在匹配位置点向前检查三个字符,符合包含在断言里的描述的三个字符。断言所包含的表达式必须是固定长度的(不能使用量词修饰),这意味着a|b是被允许的,而a*、a{3,4}是不允许的。请注意,使用后向断言永远不会从字符串开始进行匹配,你需要使用的函数可能是search(),而不是match(),因为match()是从字符串开头进行匹配,否则失败,而使用环视功能会导致该函数失败:
>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'
This example looks for a word following a hyphen:
>>> m = re.search('(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'
(?...)
如果当前位置点的前面不符合某些条件,那么从这个点开始匹配接下来的表达式。这就是所谓的前向否定断言(逆序否定环视)。类似后向否定断言,所包含的表达式必须是固定长度的字符串。在前向否定断言里的表达式可能会在字符串开始被搜索。
(?(id/name)yes-pattern|no-pattern)
如果存在并捕获成功指定编号或命名的捕获组,就会尝试匹配yes-pattern,如果捕获失败,那么尝试匹配no-pattern,|no-pattern是可省略的。例如,(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)是一个匹配电子邮件地址的表达式,将匹配'<>' 或者 ''这样的格式,但不匹配'<'。该功能在2.4版本新加入。
元字符序列由'\'和下面列出的普通字符组成。如果字符不在下列,那么正则将会匹配第二个字符,例如\$匹配'$'。
\number
引用对应编号的捕获组的内容。组编号从1开始。例如,(.+) \1匹配的是'the the'或'55 55',但不匹配'the end'。注意反向引用所引用的内容。这个元字符序列只能用来引用1至99之间的一个,如果第一个数字为0,或者是三位数,它不会被解释为反向引用,但会作为8进制数字。在字符组[]里,所有的元字符都被视为字符。
\A
匹配字符串的开始。
\b
匹配空字符串(匹配位置比较容易理解),但只在单词的开头或结尾。一个单词是由字母数字或下划线字符组成,因此一个单词的边界是空白或者非字母数字、不包括下划线。请注意,\b是指\w和\W之间的边界,因此确切的字符集定义取决于UNICODE和LOCALE编译标志的值。在字符范围内,\b表示退格符,与python的字符串兼容。
\B
匹配空字符串(匹配位置比较容易理解),但当它不在单词的开始或结尾。这是和\b相反的,也受到LOCALE和UNICODE的设置影响。
\d
当UNICODE标志没有指定,匹配任何10进制数字,相当于[0-9]。带UNICODE标志时,它会匹配任何在unicode字符集中属于数字分类的字符。
\D
当UNICODE标志没有指定,匹配任何非数字字符,相当于[^0-9]。带UNICODE标志时,它会匹配任何不在unicode字符集中属于数字分类的字符。
\s
当LOCALE和UNICODE标志没有指定时,匹配任何空白字符,这相当于[ \t\n\r\f\v]。带LOCALE标志时,它将匹配当前环境定义的空白符。如果带UNICODE标志,那么将匹配任何被划分为空白符的符号。
\S
当LOCALE和UNICODE标志没有指定时,匹配任何非空白字符,这相当于[^\t\n\r\f\v]。带LOCALE标志时,它将匹配当前环境定义的非空白符。如果带UNICODE标志,那么将匹配任何不被划分为空白符的符号。
\w
当LOCALE和UNICODE标志没有指定时,匹配任何字母数字字符、下划线,这相当于[a-zA-Z0-9_]。带LOCALE标志时,它将匹配当前环境定义的字母和[0-9_]。带UINCODE标志时,将匹配在unicode字符集里划分为字母的字符和[0-9_]。
\W
当LOCALE和UNICODE标志没有指定时,匹配任何非字母数字字符、下划线,这相当于[^a-zA-Z0-9_]。带LOCALE标志时,它将匹配除了当前环境定义的字母、[0-9_]。带UINCODE标志时,将匹配除了在unicode字符集里划分为字母的字符、[0-9_]。
\Z