本模块提供了和Perl里的正则表达式类似的功能,不关是正则表达式本身还是被搜索的字符串,都可以是Unicode字符,这点不用担心,python会处理地和Ascii字符一样漂亮。
正则表达式使用反斜杆(\)来转义特殊字符,使其可以匹配字符本身,而不是指定其他特殊的含义。这可能会和python字面意义上的字符串转义相冲突,这也许有些令人费解。比如,要匹配一个反斜杆本身,你也许要用'\\\\'来做为正则表达式的字符串,因为正则表达式要是\\,而字符串里,每个反斜杆都要写成\\。
你也可以在字符串前加上 r 这个前缀来避免部分疑惑,因为 r 开头的python字符串是 raw 字符串,所以里面的所有字符都不会被转义,比如r'\n'这个字符串就是一个反斜杆加上一字母n,而'\n'我们知道这是个换行符。因此,上面的'\\\\'你也可以写成r'\\',这样,应该就好理解很多了。可以看下面这段:
复制代码 代码如下:
> import re
> s = '\x5c' #0x5c就是反斜杆
> print s
\
> re.match('\\\\', s) #这样可以匹配
<_sre.SRE_Match object at 0xb6949e20>
> re.match(r'\\', s) #这样也可以
<_sre.SRE_Match object at 0x80ce2c0>
> re.match('\\', s) #但是这样不行
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.6/re.py", line 137, in match
return _compile(pattern, flags).match(string)
File "/usr/lib/python2.6/re.py", line 245, in _compile
raise error, v # invalid expression
sre_constants.error: bogus escape (end of line)
>
另外值得一提的是,re模块的方法,大多也就是RegexObject对象的方法,两者的区别在于执行效率。这个在最后再展开吧。
正则表达式语法
正则表达式(RE)指定一个与之匹配的字符集合;本模块所提供的函数,将可以用来检查所给的字符串是否与指定的正则表达式匹配。
正则表达式可以被连接,从而形成新的正则表达式;例如A和B都是正则表达式,那么AB也是正则表达式。一般地,如果字符串p与A匹配,q与B匹配的话,那么字符串pq也会与AB匹配,但A或者B里含有边界限定条件或者命名组操作的情况除外。也就是说,复杂的正则表达式可以用简单的连接而成。
正则表达式可以包含特殊字符和普通字符,大部分字符比如'A','a'和'0'都是普通字符,如果做为正则表达式,它们将匹配它们本身。由于正则表达式可以连接,所以连接多个普通字符而成的正则表达式last也将匹配'last'。(后面将用不带引号的表示正则表达式,带引号的表示字符串)
下面就来介绍正则表达式的特殊字符:
'.'
点号,在普通模式,它匹配除换行符外的任意一个字符;如果指定了 DOTALL 标记,匹配包括换行符以内的任意一个字符。
'^'
尖尖号,匹配一个字符串的开始,在 MULTILINE 模式下,也将匹配任意一个新行的开始。
'$'
美元符号,匹配一个字符串的结尾或者字符串最后面的换行符,在 MULTILINE 模式下,也匹配任意一行的行尾。也就是说,普通模式下,foo.$去搜索'foo1\nfoo2\n'只会找到'foo2′,但是在 MULTILINE 模式,还能找到 ‘foo1′,而且就用一个 $ 去搜索'foo\n'的话,会找到两个空的匹配:一个是最后的换行符,一个是字符串的结尾,演示:
复制代码 代码如下:
> re.findall('(foo.$)', 'foo1\nfoo2\n')
['foo2']
> re.findall('(foo.$)', 'foo1\nfoo2\n', re.MULTILINE)
['foo1', 'foo2']
> re.findall('($)', 'foo\n')
['', '']
'*'
星号,指定将前面的RE重复0次或者任意多次,而且总是试图尽量多次地匹配。
'+'
加号,指定将前面的RE重复1次或者任意多次,而且总是试图尽量多次地匹配。
'"codetitle">复制代码 代码如下:
> re.findall('<(.*)>', '<H1>title</H1>')
['H1>title</H1']
> re.findall('<(.*"codetitle">复制代码 代码如下:
> re.findall('("codetitle">复制代码 代码如下:
> m=re.match('("codetitle">复制代码 代码如下:
> re.match('<("codetitle">复制代码 代码如下:
> m = re.search('("codetitle">复制代码 代码如下:
#\u2076\和u2084分别是上标的6和下标的4,属于unicode的DIGIT
> unistr = u'\u2076\u2084abc'
> print unistr
"codetitle">复制代码 代码如下:
> re.match("c", "abcdef") # 不匹配
> re.search("c", "abcdef") # 匹配
<_sre.SRE_Match object at ...>
模块的属性和方法
re.compile(pattern[, flags])
把一个正则表达式pattern编译成正则对象,以便可以用正则对象的match和search方法。
得到的正则对象的行为(也就是模式)可以用flags来指定,值可以由几个下面的值OR得到。
以下两段内容在语法上是等效的:
复制代码 代码如下:
prog = re.compile(pattern)
result = prog.match(string)
result = re.match(pattern, string)
区别是,用了re.compile以后,正则对象会得到保留,这样在需要多次运用这个正则对象的时候,效率会有较大的提升。再用上面用过的例子来演示一下,用相同的正则匹配相同的字符串,执行100万次,就体现出compile的效率了(数据来自我那1.86G CPU的神舟本本):
复制代码 代码如下:
> timeit.timeit(
... setup='''import re; reg = re.compile('<("codetitle">复制代码 代码如下:
a = re.compile(r"""\d + # the integral part
\. # the decimal point
\d * # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")
re.search(pattern, string[, flags])
扫描string,看是否有个位置可以匹配正则表达式pattern。如果找到了,就返回一个MatchObject的实例,否则返回None,注意这和找到长度为0的子串含义是不一样的。搜索过程受flags的影响。
re.match(pattern, string[, flags])
如果字符串string的开头和正则表达式pattern匹配的话,返回一个相应的MatchObject的实例,否则返回None
注意:要在字符串的任意位置搜索的话,需要使用上面的search()。
re.split(pattern, string[, maxsplit=0])
用匹配pattern的子串来分割string,如果pattern里使用了圆括号,那么被pattern匹配到的串也将作为返回值列表的一部分。如果maxsplit不为0,则最多被分割为maxsplit个子串,剩余部分将整个地被返回。
复制代码 代码如下:
> re.split('\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
> re.split('(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
> re.split('\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
如果正则有圆括号,并且可以匹配到字符串的开始位置的时候,返回值的第一项,会多出一个空字符串。匹配到字符结尾也是同样的道理:
复制代码 代码如下:
> re.split('(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']
注意,split不会被零长度的正则所分割,例如:
复制代码 代码如下:
> re.split('x*', 'foo')
['foo']
> re.split("(", "foo\n\nbar\n")
['foo\n\nbar\n']
re.findall(pattern, string[, flags])
以列表的形式返回string里匹配pattern的不重叠的子串。string会被从左到右依次扫描,返回的列表也是从左到右一次匹配到的。如果pattern里含有组的话,那么会返回匹配到的组的列表;如果pattern里有多个组,那么各组会先组成一个元组,然后返回值将是一个元组的列表。
由于这个函数不会涉及到MatchObject之类的概念,所以,对新手来说,应该是最好理解也最容易使用的一个函数了。下面就此来举几个简单的例子:
复制代码 代码如下:
#简单的findall
> re.findall('\w+', 'hello, world!')
['hello', 'world']
#这个返回的就是元组的列表
> re.findall('(\d+)\.(\d+)\.(\d+)\.(\d+)', 'My IP is 192.168.0.2, and your is 192.168.0.3.')
[('192', '168', '0', '2'), ('192', '168', '0', '3')]
re. finditer(pattern, string[, flags])
和上面的findall()类似,但返回的是MatchObject的实例的迭代器。
还是例子说明问题:
复制代码 代码如下:> for m in re.finditer('\w+', 'hello, world!'):
... print m.group()
...
hello
world
re.sub(pattern, repl, string[, count])
替换,将string里,匹配pattern的部分,用repl替换掉,最多替换count次(剩余的匹配将不做处理),然后返回替换后的字符串。如果string里没有可以匹配pattern的串,将被原封不动地返回。repl可以是一个字符串,也可以是一个函数(也可以参考我以前的例子)。如果repl是个字符串,则其中的反斜杆会被处理过,比如 \n 会被转成换行符,反斜杆加数字会被替换成相应的组,比如 \6 表示pattern匹配到的第6个组的内容。
例子:
复制代码 代码如下:
> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
... r'static PyObject*\npy_\1(void)\n{',
... 'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'
如果repl是个函数,每次pattern被匹配到的时候,都会被调用一次,传入一个匹配到的MatchObject对象,需要返回一个字符串,在匹配到的位置,就填入返回的字符串。
例子:
复制代码 代码如下:
> def dashrepl(matchobj):
... if matchobj.group(0) == '-': return ' '
... else: return '-'
> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
零长度的匹配也会被替换,比如:
复制代码 代码如下:
> re.sub('x*', '-', 'abcxxd')
'-a-b-c-d-'
特殊地,在替换字符串里,如果有\g这样的写法,将匹配正则的命名组(前面介绍过的,("codetitle">复制代码 代码如下:
> re.sub('-(\d+)-', '-\g<1>0\g<0>', 'a-11-b-22-c')
'a-110-11-b-220-22-c'
re.subn(pattern, repl, string[, count])
跟上面的sub()函数一样,只是它返回的是一个元组 (新字符串, 匹配到的次数)
,还是用例子说话:
复制代码 代码如下:
> re.subn('-(\d+)-', '-\g<1>0\g<0>', 'a-11-b-22-c')
('a-110-11-b-220-22-c', 2)
re.escape(string)
把string中,除了字母和数字以外的字符,都加上反斜杆。
复制代码 代码如下:
> print re.escape('abc123_@#$')
abc123\_\@\#\$
exception re.error
如果字符串不能被成功编译成正则表达式或者正则表达式在匹配过程中出错了,都会抛出此异常。但是如果正则表达式没有匹配到任何文本,是不会抛出这个异常的。
正则对象
正则对象由re.compile()返回。它有如下的属性和方法。
match(string[, pos[, endpos]])
作用和模块的match()函数类似,区别就是后面两个参数。
pos是开始搜索的位置,默认为0。endpos是搜索的结束位置,如果endpos比pos还小的话,结果肯定是空的。也就是说只有pos 到 endpos-1 位置的字符串将会被搜索。
例子:
复制代码 代码如下:
> pattern = re.compile("o")
> pattern.match("dog") # 开始位置不是o,所以不匹配
> pattern.match("dog", 1) # 第二个字符是o,所以匹配
<_sre.SRE_Match object at ...>
search(string[, pos[, endpos]])
作用和模块的search()函数类似,pos和endpos参数和上面的match()函数类似。
split(string[, maxsplit=0])
findall(string[, pos[, endpos]])
finditer(string[, pos[, endpos]])
sub(repl, string[, count=0])
subn(repl, string[, count=0])
这几个函数,都和模块的相应函数一致。
flags
编译本RE时,指定的标志位,如果未指定任何标志位,则为0。
复制代码 代码如下:
> pattern = re.compile("o", re.S|re.U)
> pattern.flags
48
groups
RE所含有的组的个数。
groupindex
一个字典,定义了命名组的名字和序号之间的关系。
例子:这个正则有3个组,如果匹配到,第一个叫区号,最后一个叫分机号,中间的那个未命名
复制代码 代码如下:> pattern = re.compile("(")
> pattern.groups
3
> pattern.groupindex
{'fenjihao': 3, 'quhao': 1}
pattern
建立本RE的原始字符串,相当于源代码了,呵呵。
还是上面这个正则,可以看到,会原样返回:
复制代码 代码如下:
> print pattern.pattern
("codetitle">复制代码 代码如下:
> m = re.match('a=(\d+)', 'a=100')
> m.expand('above a is \g<1>')
'above a is 100'
> m.expand(r'above a is \1')
'above a is 100'
group([group1, ...])
返回一个或多个子组。如果参数为一个,就返回一个子串;如果参数有多个,就返回多个子串注册的元组。如果不传任何参数,效果和传入一个0一样,将返回整个匹配。如果某个groupN未匹配到,相应位置会返回None。如果某个groupN是负数或者大于group的总数,则会抛出IndexError异常。
复制代码 代码如下:
> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
> m.group(0) # 整个匹配
'Isaac Newton'
> m.group(1) # 第一个子串
'Isaac'
> m.group(2) # 第二个子串
'Newton'
> m.group(1, 2) # 多个子串组成的元组
('Isaac', 'Newton')
如果有其中有用("codetitle">复制代码 代码如下:
> m = re.match(r"(", "Malcolm Reynolds")
> m.group('first_name')
'Malcolm'
> m.group('last_name')
'Reynolds'
如果某个组被匹配到多次,那么只有最后一次的数据,可以被提取到:
复制代码 代码如下:
> m = re.match(r"(..)+", "a1b2c3") # 匹配到3次
> m.group(1) # 返回的是最后一次
'c3'
groups([default])
返回一个由所有匹配到的子串组成的元组。default参数,用于给那些没有匹配到的组做默认值,它的默认值是None
例如:
复制代码 代码如下:
> m = re.match(r"(\d+)\.(\d+)", "24.1632")
> m.groups()
('24', '1632')
default的作用:
复制代码 代码如下:
> m = re.match(r"(\d+)\.", "24")
> m.groups() # 第二个默认是None
('24', None)
> m.groups('0') # 现在默认是0了
('24', '0')
groupdict([default])
返回一个包含所有命名组的名字和子串的字典,default参数,用于给那些没有匹配到的组做默认值,它的默认值是None,例如:
复制代码 代码如下:
> m = re.match(r"(", "Malcolm Reynolds")
> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
start([group])
end([group])
返回的是:被组group匹配到的子串在原字符串中的位置。如果不指定group或group指定为0,则代表整个匹配。如果group未匹配到,则返回 -1。
对于指定的m和g,m.group(g)和m.string[m.start(g):m.end(g)]等效。
注意:如果group匹配到空字符串,m.start(group)和m.end(group)将相等。
例如:
复制代码 代码如下:
> m = re.search('b(c"codetitle">复制代码 代码如下:
> email = "tony@tiremove_thisger.net"
> m = re.search("remove_this", email)
> email[:m.start()] + email[m.end():]
'tony@tiger.net'
span([group])
返回一个元组: (m.start(group), m.end(group))
pos
就是传给RE对象的search()或match()方法的参数pos,代表RE开始搜索字符串的位置。
endpos
就是传给RE对象的search()或match()方法的参数endpos,代表RE搜索字符串的结束位置。
lastindex
最后一次匹配到的组的数字序号,如果没有匹配到,将得到None。
例如:(a)b、((a)(b))和((ab))正则去匹配'ab'的话,得到的lastindex为1。而用(a)(b)去匹配'ab'的话,得到的lastindex为2。
lastgroup
最后一次匹配到的组的名字,如果没有匹配到或者最后的组没有名字,将得到None。
re
得到本Match对象的正则表达式对象,也就是执行search()或match()的对象。
string
传给search()或match()的字符串。
后面的例子就略了吧,文中已经加了很多我自己的例子了,需要更多例子的话,参照英文原文吧(https://docs.python.org/2/library/re.html)。
PS:关于正则表达式,这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:
JavaScript正则表达式在线测试工具:
http://tools.jb51.net/regex/javascript
正则表达式在线生成工具:
http://tools.jb51.net/regex/create_reg