本文基于Python官方手册进行翻译和整理,包括对正则表达式的简介、Python正则库API、以及一些使用样例。
参考手册:
http://docs.python.org/2/library/re.html?highlight=re#re
http://docs.python.org/2/howto/regex.html#regex-howto
简介
Python中主要使用re模块进行正则表达式匹配,re使用perl风格的语法,并对8字节ASCII码以及Unicode编码都提供了良好的支持。
在常规字符串中使用正则表达式是一件比较麻烦的事,拿匹配字符 \
来说,因为 \
是正则表达式中的特殊字符,对其匹配需要使用 \\
,而'\'又是字符串的转义字符,所以需要用 \\\\
来匹配一个 \
。是不是很麻烦?这样写出来的正则表达式又长又难懂,好在Python为我们提供了原始字符串————在字符串前加r,原始字符串中的字符不会被转义,可以说是所见即所得,比如 r"\n"
就表示了字符串"\n"而不是回车。因此,大多数Python程序员基本都只用原始字符串来表示模式串。
正则表达式关键字
正则表达式关键字是指用来进行匹配的字符,这些字符可以匹配特定的文本串。需要注意的是,如果要使这些字符表达原意,要用 \
进行转义。此外,需要转义的还包括一些分隔符,如 (
, |
等。
这些关键字如下
基本关键字 . 常规模式匹配除了"\n"外的所有字符,DOTALL模式下"\n"也会被匹配 ^ 匹配字符串的开头,MULTILINE模式下会匹配每行开头 $ 匹配字符串的即为,MULTILINE模式下会匹配行尾 * 匹配*号前面的内容0次至无限次 \ 转义符,在匹配字符*号等特殊字符时使用 | 或,A|B表示只要A和B中任意匹配一个 [] 匹配方括号内字符集中的任一字符,注意里面的特殊字符会失去特殊意义,以及^和-要进行转义 量词 + 匹配+号前内容1次至无限次 ? 匹配?号前内容0次到1次 {m} 匹配前面的内容m次 {m,n} 匹配前面的内容m到n次 *?,+?,??,{m,n}? 前面的*,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配 分组匹配 (..) 分组,首先是会在group函数中得到这个分组,其次是会成为基本重复单位(+,?,*等) (?..) ()的扩展形式,()中的内容不会被作为分组记录(?P<name>除外),扩展有以下几种 (?:..) 最基础的扩展,()内的内容不会被作为分组记录 (?P<name>) 该分组会被作为分组记录,并且分组名为name (?P=name) 后向引用(?P<name>)中的内容 (?#...) 注释,括号中的内容会被忽略 (?=...) 之后的字符串需要匹配括号中的内容,但不消耗字符串,比如a(?=b)只能匹配ab,接下来从b开始继续匹配 (?!...) 之后的内容不能匹配括号中的内容,但不消耗字符串 (?<=...) 之前的字符串需要匹配括号中的内容,但不消耗字符串 (?<!...) 之前的内容不能匹配括号中的内容,但不消耗字符串 (?(id/name)yes-pattern|no-pattern) 如果指定的id或name的分组匹配到内容,则使用yes-pattern匹配,否则使用no-pattern (?iLmsux) 用于改变字符串的匹配模式(后面会说),只能用在开头,也可以在compile时使用flag参数来指定匹配模式 "\"匹配 \number 后向引用\number中匹配的内容 使用 \A 匹配字符串开头 \Z 匹配字符串结尾 \w 匹配[a-zA-Z0-9_] \W 匹配[^\w] \b 匹配[^\w]和[\w]之间的字符,也就是匹配字符串边界 \B 匹配[^\b] \d 匹配[0-9] \D 匹配[^\d] \s 匹配[ \t\n\r\f\v],即所有空白字符 \S 匹配[^\s] 另外有一些常规转义符,包括\a \b \f \n \r \t \v \x \\,注意这里\b匹配backspace时,必须在[]中使用 |
Python正则库API
选项
re库API中,一般都有flags参数,通过该参数指定正则表达式选项。传递时一般使用简写,比如开启DOTALL和MULTILINE使用 re.I|re.M
A ASCII 使\w\W\b\B\d\D匹配ASCII字符 I IGNORECASE 忽略大小写 L LOCALE 使\w\W\b\B匹配本地字符集 M MULTILINE 多行模式,"^" 匹配每行开头,"$"匹配每行结尾 S DOTALL "." 匹配所有字符,包括"\n" X VERBOSE 详细模式,忽略空白可以加入注释 U UNICODE 使\w\W\b\B\d\D匹配unicode字符集 |
API速查
这里只是列出API,便于查阅,后面会详细介绍API的使用。建议先跳过这一段,直接看后面的Sample,再回过头来看这一段。
API分为三组,第一组是模块API(Module Contents),通过 re.xx()
使用;第二组是表达式API(Regular Expression Objects), re.complie()
函数会返回一个表达式对象,通过该对象使用的函数;第三组是匹配对象API(Match Objects),像search这些函数都会返回一个匹配结果,这组API用于操作结果集。
re库对于很多函数,例如match,都提供了两种调用方式,一是直接通过re库调用,将正则表达式作为参数,二是先用complie编译表达式,通过返回的对象调用,方法二在正则表达式会被多次使用时会减少重复编译花费的时间。
|
使用re模块
在整个re库中用到的对象一共有三个,分别是顶级的re对象、re.compile函数返回的Pattern对象、match、serach等函数返回的Match对象。而使用re库的方法也有两种,一种是直接使用re对象的方法,一种是先complie表达式返回Pattern对象,再使用Pattern对象来操作,这两者的区别是后者可以减少重复编译的时间。实际上即使不使用compile,re库也会缓存一些近期使用过的正则表达式编译结果,所以这里不用太担心两者的效率差别。
compile
作用就是编译一个表达式,以下两段代码作用相同
# use compile p = re.compile(r'ab*') r = p.match('abcc') print r.group(0) # don't use compile r = re.match(r'ab*', 'abcc') print r.group(0) # output ab ab |
匹配对象(Match Object)
所谓匹配对象,就是match,search,findall等搜索操作返回的结果对象,下面举个简单的例子看下匹配对象(Match Object)中各种方法的使用
我们使用正则表达式 ([a-z]+) (?P<digit>[\d]+)
来匹配类似于 abc 123
这样的串。我们捕捉字符串组和数字组,并给数字组命名为digit。
p = re.compile(r'([a-z]+) (?P<digit>[\d]+)') r = p.search('name su, id 123') print 're :', r.re #re object used print 'string :', r.string #string print 'pos :', r.pos #search start pos print 'endpos :', r.endpos #search end pos print 'lastindex :', r.lastindex #last group index print 'lastgroup :', r.lastgroup #last group name print 'groups :', r.groups() #all group from index 1 print 'group :', r.group(0, 1, 2) #group index 0, 1, 2 print 'groupdict :', r.groupdict() #group name dict print 'start :', r.start(2) #start pos of group 2 print 'end :', r.end("digit") #end pos of group digit print 'span(num) :', r.span(2) #range of group 2 print 'span(name):', r.span("digit") #range of group digit print 'expand :', r.expand(r'\g<digit> \1') #swap group 1 with 2 # output re : <_sre.SRE_Pattern object at 0x015AE860> string : name su, id 123 pos : 0 endpos : 15 lastindex : 2 lastgroup : digit groups : ('id', '123') group : ('id 123', 'id', '123') groupdict : {'digit': '123'} start : 12 end : 15 span(num) : (12, 15) span(name): (12, 15) expand : 123 id |
可以看到,对于有名组,我们不仅可以用Index来引用该组,还可以使用组名来引用,这在分组较多时会十分有用。在expand函数中使用了后向引用, \1
表示 group 1
, \g<digit>
表示 group digit
。
search VS match
search和match的作用是相近的,都是在字符串中匹配一个模式串,唯一的不同是match从首字符开始搜索,而search不要求。search在正则前加 ^
可以达到和match相同的作用。
依然用前面的正则表达式,结果如下。
r = re.search(r'([a-z]+) (?P<digit>[\d]+)', 'name id 123') print r r = re.match(r'([a-z]+) (?P<digit>[\d]+)', 'name id 123') print r r = re.search(r'^([a-z]+) (?P<digit>[\d]+)', 'name id 123') print r # output <_sre.SRE_Match object at 0x016954A0> None None |
sub
sub用于替换匹配到的内容,它的特色是替换参数可以是一个函数,我们看一下如何使用函数来进行替换。
这个例子是将所有的数字从十进制转换成十六进制数。
这里也可以用lambda表达式使程序更加简洁
split
它是字符串split函数的强化版,可以使用正则表达式
一个简单的例子,分离字符串中的数字。注意如果使用了捕获括号,捕获的内容也会出现在结果中。
findall && finditer
search的加强版,findall返回所有结果,finditer返回一个迭代器,延用上面的例子。
More
关于Python中的正则表达式,基本就是这些内容了。能写出一个性能良好并且正确的正则表达式,也是对一个程序员的基本要求了,本文并没有详细介绍如何使用正则表达式,如果想用好正则表达式,还要进一步学习有关正则表达式的高级知识。