正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。

  re 模块使 Python 语言拥有全部的正则表达式功能。compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。

  正则表达式的大致匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。如果表达式中有量词或边界,这个过程会稍微有一些不同,但也是很好理解的,看下图中的示例以及自己多使用几次就能明白。

pyre_ebb9ce1c-e5e8-4219-a8ae-7ee620d5f9f

re模块

  Python通过re模块提供对正则表达式的支持。使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使用Match实例获得信息,进行其他的操作。

  re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

re.match(pattern, string, flags=0)

  函数参数说明:pattern匹配的正则表达式;string要匹配的字符串;flags标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等。匹配成功re.match方法返回一个匹配的对象,否则返回None。我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。

  re.match #从开始位置开始匹配,如果开头没有则无

  re.search #搜索整个字符串

  re.findall #搜索整个字符串,返回一个list

r(raw)用在pattern之前,表示单引号中的字符串为原生字符,不会进行任何转义
re.match(r'l','liuyan1').group()       #返回l
re.match(r'y','liuyan1')               #返回None
re.search(r'y','liuyan1').group()      #返回y

  正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:

re.I使匹配对大小写不敏感
re.L做本地化识别(locale-aware)匹配
re.M多行匹配,影响 ^ 和 $
re.S使 . 匹配包括换行在内的所有字符
re.U根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B
re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解

  为什么要用 r’ ..‘ 字符串( raw 字符串)? 由于正则式的规则也是由一个字符串定义的,而在正则式中大量使用转义字符 ’/’ ,如果不用 raw 字符串,则在需要写一个 ’/’ 的地方,你必须得写成 ’//’, 那么在要从目标字符串中匹配一个 ’/’ 的时候,你就得写上 4 个 ’/’ 成为 ’’ !这当然很麻烦,也不直观,所以一般都使用 r’’ 来定义规则字符串。当然,某些情况下,可能不用 raw 字符串比较好。

re.search(r'[a-z]+','liuyaN1234ab9').group()           #返回'liuya'
re.search(r'[a-z]+','liuyaN1234ab9', re.I).group()     #返回'liuyaN',对大小写不敏感
#如果匹配成功,则打印m,否则返回Null
if re.match(r'[0-9]','a'):print 'm'
#用空格分割
re.split(r'\s+', 'a b   c')                           #返回:['a', 'b', 'c', 'd']
#用逗号分隔
re.split(r'[\s\,]+', 'a,b, c  d')                      #返回:['a', 'b', 'c', 'd']
import re
print(re.match('www', 'www.baidu.com').span())  # 在起始位置匹配
print(re.match('com', 'www.baidu.com'))         # 不在起始位置匹配
line = "Cats are smarter than dogs" matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I) if matchObj:  
  print "matchObj.group() : ", matchObj.group()   print "matchObj.group(1) : ", matchObj.group(1)   print "matchObj.group(2) : ", matchObj.group(2)
  else:  
  print "No match!!"
运行结果:
(0, 3)
None
matchObj.group() :  Cats are smarter than dogs
matchObj.group(1) :  Cats
matchObj.group(2) :  smarter

re.compile(strPattern[, flag])

  这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。 
可选值有:

  • re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)

  • M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图)

  • S(DOTALL): 点任意匹配模式,改变'.'的行为

  • L(LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定

  • U(UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性

  • X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。以下两个正则表达式是等价的:

import re
pattern = re.compile(r'hello')
match = pattern.match('hello world!')
if match:
   print match.group()
   
运行结果:
hello

(1)功能字符 :     ‘.’ ‘*’ ‘+’ ‘|’ ‘?’ ‘^’ ‘$’ ‘/’ 等,它们有特殊的功能含义。特别是 ’/’ 字符,它是转义引导符号,跟在它后面的字符一般有特殊的含义。

(2)规则分界符: ‘[‘ ‘]’ ‘ ( ’ ‘ ) ’ ‘{‘ ‘}’ 等,也就是几种括号了。

(3)预定义转义字符集: “/d”  “/w” “/s” 等等,它们是以字符 ’/’ 开头,后面接一个特定字符的形式,用来指示一个预定义好的含义。

(4)其它特殊功能字符: ’#’ ‘!’ ‘:’ ‘-‘ 等,它们只在特定的情况下表示特殊的含义,比如 (?# …) 就表示一个注释,里面的内容会被忽略。

‘[‘  ‘]’字符集合设定符

  首先说明一下字符集合设定的方法。由一对方括号括起来的字符,表明一个字符集合,能够匹配包含在其中的任意一个字符。比如 [abc123] ,表明字符 ’a’ ‘b’ ‘c’ ‘1’ ‘2’ ‘3’ 都符合它的要求。可以被匹配。

  在 ’[‘ ‘]’ 中还可以通过 ’-‘ 减号来指定一个字符集合的范围,比如可以用 [a-zA-Z] 来指定所以英文字母的大小写,因为英文字母是按照从小到大的顺序来排的。你不可以把大小的顺序颠倒了,比如写成 [z-a] 就不对了。

  如果在 ’[‘ ‘]’ 里面的开头写一个 ‘^’ 号,则表示取非,即在括号里的字符都不匹配。如 [^a-zA-Z] 表明不匹配所有英文字母。但是如果 ‘^’ 不在开头,则它就不再是表示取非,而表示其本身,如 [a-z^A-Z] 表明匹配所有的英文字母和字符 ’^’ 。

‘|’或规则

  将两个规则并列起来,以‘ | ’连接,表示只要满足其中之一就可以匹配。比如[a-zA-Z]|[0-9] 表示满足数字或字母就可以匹配,这个规则等价于 [a-zA-Z0-9]。

import re
s = ‘I have a dog , I have a cat’
re.findall( r’I have a (?:dog|cat)’ , s )
re.findall( r’I have a dog|cat’ , s )

运行结果:
['I have a dog', 'I have a cat']  
['I have a dog', 'cat']        

(1):它在 ’[‘ ‘]’ 之中不再表示或,而表示他本身的字符。如果要在 ’[‘ ‘]’ 外面表示一个 ’|’ 字符,必须用反斜杠引导,即 ’/|’。

(2):它的有效范围是它两边的整条规则,比如‘ dog|cat’ 匹配的是‘ dog’ 和 ’cat’ ,而不是 ’g’ 和 ’c’ 。如果想限定它的有效范围,必需使用一个无捕获组 ‘(?: )’ 包起来。比如要匹配 ‘ I have a dog’ 或 ’I have a cat’ ,需要写成 r’I have a (?:dog|cat)’ ,而不能写成 r’I have a dog|cat’。

‘.’匹配所有字符

  匹配除换行符 ’/n’ 外的所有字符。如果使用了 ’S’ 选项,匹配包括 ’/n’ 的所有字符。

import re
s=’123 /n456 /n789’
findall(r‘.+’,s)
re.findall(r‘.+’ , s , re.S)

运行结果:
['123', '456', '789']
['123/n456/n789']

‘^’ 和 ’$’ 匹配字符串开头和结尾

  注意 ’^’ 不能在‘ [ ] ’中,否则含意就发生变化,具体请看上面的 ’[‘ ‘]’ 说明。 在多行模式下,它们可以匹配每一行的行首和行尾。具体请看后面 compile 函数说明的 ’M’ 选项部分。

‘/d’ 匹配数字

  这是一个以 ’/’ 开头的转义字符, ’/d’ 表示匹配一个数字,即等价于 [0-9]

‘/D’ 匹配非数字

  这个是上面的反集,即匹配一个非数字的字符,等价于 [^0-9] 。注意它们的大小写。下面我们还将看到 Python 的正则规则中很多转义字符的大小写形式,代表互补的关系。这样很好记。

‘/w’ 匹配字母和数字

  匹配所有的英文字母和数字,即等价于 [a-zA-Z0-9] 。

‘/W’ 匹配非英文字母和数字

  即 ’/w’ 的补集,等价于 [^a-zA-Z0-9] 。

‘/s’ 匹配间隔符

  即匹配空格符、制表符、回车符等表示分隔意义的字符,它等价于 [ /t/r/n/f/v] 。(注意最前面有个空格 )

‘/S’ 匹配非间隔符

  即间隔符的补集,等价于 [^ /t/r/n/f/v]

‘/A’ 匹配字符串开头

  匹配字符串的开头。它和 ’^’ 的区别是, ’/A’ 只匹配整个字符串的开头,即使在 ’M’ 模式下,它也不会匹配其它行的很首。

‘/Z’ 匹配字符串结尾

  匹配字符串的结尾。它和 ’$’ 的区别是, ’/Z’ 只匹配整个字符串的结尾,即使在 ’M’ 模式下,它也不会匹配其它各行的行尾。

‘/b’ 匹配单词边界

  它匹配一个单词的边界,比如空格等,不过它是一个‘ 0 ’长度字符,它匹配完的字符串不会包括那个分界的字符。而如果用 ’/s’ 来匹配的话,则匹配出的字符串中会包含那个分界符。

‘/B’ 匹配非边界

  和 ’/b’ 相反,它只匹配非边界的字符。它同样是个 0 长度字符。

‘(?:)’ 无捕获组

  当你要将一部分规则作为一个整体对它进行某些操作,比如指定其重复次数时,你需要将这部分规则用 ’(?:’ ‘)’ 把它包围起来,而不能仅仅只用一对括号,那样将得到绝对出人意料的结果。

‘(?# )’ 注释

  Python 允许你在正则表达式中写入注释,在 ’(?#’ ‘)’ 之间的内容将被忽略。

‘*’   0 或多次匹配

  表示匹配前面的规则 0 次或多次。

‘+’   1 次或多次匹配

  表示匹配前面的规则至少 1 次,可以多次匹配

‘?’   0 或 1 次匹配

  只匹配前面的规则 0 次或 1 次。

‘{m}’      精确匹配 m 次

‘{m,n}’   匹配最少 m 次,最多 n 次。 (n>m)

  如果你只想指定一个最少次数或只指定一个最多次数,你可以把另外一个参数空起来。比如你想指定最少 3 次,可以写成 {3,} (注意那个逗号),同样如果只想指定最大为 5 次,可以写成 { , 5} ,也可以写成 {0,5} 。

‘*?’ ‘+?’ ‘??’ 最小匹配

  ‘*’ ‘+’ ‘?’ 通常都是尽可能多的匹配字符。有时候我们希望它尽可能少的匹配。

‘(?<=…)’ 前向界定

  括号中 ’…’ 代表你希望匹配的字符串的前面应该出现的字符串。

‘(?=…)’  后向界定

  括号中的 ’…’ 代表你希望匹配的字符串后面应该出现的字符串。

‘(?<!...)’ 前向非界定

 只有当你希望的字符串前面不是’…’ 的内容时才匹配

‘(?!...)’ 后向非界定

  只有当你希望的字符串后面不跟着 ’…’ 内容时才匹配。

‘(‘’)’       无命名组

  最基本的组是由一对圆括号括起来的正则式。

‘(?P<name>…)’ 命名组

  ‘(?P’ 代表这是一个 Python 的语法扩展 ’<…>’ 里面是你给这个组起的名字,比如你可以给一个全部由数字组成的组叫做 ’num’ ,它的形式就是 ’(?P<num>/d+)’ 。

‘(?P=name)’ 调用已匹配的命名组

  要注意,再次调用的这个组是已被匹配的组,也就是说它里面的内容是和前面命名组里的内容是一样的。

‘/number’             通过序号调用已匹配的组

  正则式中的每个组都有一个序号,序号是按组从左到右,从 1 开始的数字

‘(?( id/name )yes-pattern|no-pattern)’ 判断指定组是否已匹配,执行相应的规则

  这个规则的含义是,如果 id/name 指定的组在前面匹配成功了,则执行 yes-pattern 的正则式,否则执行 no-pattern 的正则式。

import re
s= '12 34/n56 78/n90'
re.findall( r'^/d+' , s , re.M )          # 匹配位于行首的数字
re.findall( r’/A/d+’, s , re.M )          # 匹配位于字符串开头的数字
re.findall( r'/d+$' , s , re.M )          # 匹配位于行尾的数字
re.findall( r’/d+/Z’ , s , re.M )         # 匹配位于字符串尾的数字
a =  'abc abcde bc bcd'
re.findall( r’/bbc/b’ , a)             # 匹配一个单独的单词 ‘bc’ ,而当它是其它单词的一部分的时候不匹配
re.findall( r’/sbc/s’ , a )            #匹配一个单独的单词 ‘bc’
re.findall( r’/Bbc/w+’ , a )           #匹配包含 ’bc’ 但不以 ’bc’ 为开头的单词
b=’ababab abbabb aabaab’
re.findall( r’/b(?:ab)+/b’ , b)
re.findall( r’/b(ab)+/b’ , b)
c = ‘ aaa bbb111 cc22cc 33dd ‘
re.findall( r’/b[a-z]+/d*/b’ , c )       #必须至少 1 个字母开头,以连续数字结尾或没有数字
re.findall( r’[a-z]+/d*’ , c )
d = ‘ 123 10e3 20e4e4 30ee5 ‘
re.findall( r’ /b/d+[eE]?/d*/b’ , d )
num= ‘ 1 22 333 4444 55555 666666 ‘
re.findall( r’/b/d{3}/b’ , num )           # a : 3 位数
re.findall( r’/b/d{2,4}/b’ , num )         # b: 2 位数到 4 位数
re.findall( r’/b/d{5,}/b’, num )           # c: 5 位数以上的数
re.findall( r’/b/d{1,4}/b’ , num )         # 4 位数以下的数
e =r ‘/* part 1 */ code /* part 2 */’
re.findall( r’//*.*/*/’ , e )
re.findall( r’//*.*?/*/’ , e )             # 在 * 后面加上 ? ,表示尽可能少的匹配
f=r’/* comment 1 */  code  /* comment 2 */’
re.findall( r’(?<=//*).+?(?=/*/)’ , f )
g = ‘aaa111aaa , bbb222 , 333ccc ‘
re.findall( r’(?<=[a-z]+)/d+(?=[a-z]+)' , g )          # 错误的用法
re.findall( r’/d+(?=[a-z]+)’, g )
re.findall (r'[a-z]+(/d+)[a-z]+' , g )
re.findall( r’/d+(?!/w+)’ , g )
re.findall( r’/d+(?![a-z]+)’ , g )
h = ‘aaa111aaa , bbb222 , 333ccc ‘
re.findall (r'[a-z]+(/d+)[a-z]+' , h )
k='aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'
re.findall( r'([a-z]+)/d+([a-z]+)' , k )             # 找出中间夹有数字的字母
re.findall( r '(?P<g1>[a-z]+)/d+(?P=g1)' , k )       # 找出被中间夹有数字的前后同样的字母
re.findall( r'[a-z]+(/d+)([a-z]+)' , k )             # 找出前面有字母引导,中间是数字,后面是字母的字符串中的中间的数字和后面的字母
re.findall( r’([a-z]+)/d+/1’ , k )
m='111aaa222aaa111 , 333bbb444bb33'
re.findall( r'(/d+)([a-z]+)(/d+)(/2)(/1)' , m )  
n='<usr1@mail1>  usr2@maill2'  
re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , n )
u='<usr1@mail1>  usr2@maill2 <usr3@mail3   usr4@mail4>  < usr5@mail5 '
re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , u )


运行结果:
['12', '56', '90']
['12']
['34', '78', '90']
['90']
['bc']                               #只找到了那个单独的 ’bc’
[' bc ']                             #只找到那个单独的 ’bc’ ,不过注意前后有两个空格,可能有点看不清楚
['bcde']                             #成功匹配了 ’abcde’ 中的 ’bcde’ ,而没有匹配 ’bcd’
['ababab']
['ab']
['aaa', 'bbb111']
['aaa', 'bbb111', 'cc22', 'cc', 'dd']     # 把单词给拆开了
['123', '10e3']
['333']
['22', '333', '4444']
['55555', '666666']
['1', '22', '333', '4444']
[‘/* part 1 */ code /* part 2 */’]
['/* part 1 */', '/* part 2 */']
[' comment 1 ', ' comment 2 ']
['111', '333']
['111']
['222']
['11', '222', '33']
['111']
[('aaa', 'aaa'), ('fff', 'ggg')]
['aaa']
[('111', 'aaa'), ('777', 'ggg')]
['aaa']
[('111', 'aaa', '222', 'aaa', '111')]
[('<', 'usr1@mail1'), ('', 'usr2@maill2')]
[('<', 'usr1@mail1'), ('', 'usr2@maill2'), ('', 'usr3@mail3'), ('', 'usr4@mail4'), ('', 'usr5@mail5')]

match 与 search

match( rule , targetString [,flag] )

search( rule , targetString [,flag] )

  match 从字符串的开头开始匹配,如果开头位置没有匹配成功,就算失败了;而 search 会跳过开头,继续向后寻找是否有匹配的字符串。针对不同的需要,可以灵活使用这两个函数。

s= 'Tom:9527 , Sharry:0003'
m=re.match( r'(?P<name>/w+):(?P<num>/d+)' , s )
m.group()
m.groups()
m.group(‘name’)
m.group(‘num’)

运行结果:
'Tom:9527'
('Tom', '9527')
'Tom'
'9527'

finditer( rule , target [,flag] )

参数同 findall,返回一个迭代器

  finditer 函数和 findall 函数的区别是, findall 返回所有匹配的字符串,并存为一个列表,而 finditer 则并不直接返回这些字符串,而是返回一个迭代器。关于迭代器,解释起来有点复杂,举例如下:

s=’111 222 333 444’
for i in re.finditer(r’/d+’ , s ):
   print i.group(),i.span()        # 打印每次得到的字符串和起始结束位置
   
运行结果:
111 (0, 3)
222 (4, 7)
333 (8, 11)
444 (12, 15)

  finditer 返回了一个可调用的对象,使用 for i in finditer() 的形式,可以一个一个的得到匹配返回的 Match 对象。这在对每次返回的对象进行比较复杂的操作时比较有用。

split( rule , target [,maxsplit] )

  切片函数。使用指定的正则规则在目标字符串中查找匹配的字符串,用它们作为分界,把字符串切片。第一个参数是正则规则,第二个参数是目标字符串,第三个参数是最多切片次数,返回一个被切完的子字符串的列表。

这个函数和 str 对象提供的 split 函数很相似。如我们想把上例中的字符串被 ’,’ 分割开,同时要去掉逗号前后的空格:

s=’ I have a dog   ,   you have a dog  ,  he have a dog ‘
re.split( ‘/s*,/s*’ , s )
输出
[' I have a dog', 'you have a dog', 'he have a dog '