python:正则表达式

一、概述

基本规则

规则说明
[ ]字符集合设定符
|或规则
.匹配所有字符
^匹配字符串开头
$匹配字符串结尾
\d匹配数字
\D匹配非数字
\w匹配字母和数字
\W匹配非英文字母和数字
\s匹配间隔符
\S匹配非间隔符
\A匹配字符串开头
\Z匹配字符串结尾
\b匹配单词边界
\B匹配非边界
(?: )无捕获组
(?# )注释
(?iLmsux)编译选项指定
*0次或多次匹配
+1次或多次匹配
?0次或1次匹配
{m}精确匹配 m 次
{m,n}匹配最少 m 次,最多 n 次 (n>m)
*? +? ??最小匹配

前向界定与后向界定

界定说明
(?<=…)前向界定
(?=…)后向界定
(?<!..)前向非界定
(?!..)后向非界定

组的基本知识

类别说明
(’ ')无命名组
(?P…)命名组
\number通过序号调用已匹配的组
(?(id/name)yes-patternno-pattern)
Pattern = compile(rule[,flag])

将正则规则编译成一个 Pattern 对象,以供接下来使用。

compile 函数的规则标志

标志作用
I IGNORECASE忽略大小写区别
L LOCAL字符集本地化。这个功能是为了支持多语言版本的字符集使用环境的,比如在转义符\w ,在英文环境下,它代表[a-zA-Z0-9] ,即所有英文字符和数字。如果在一个法语环境下使用,缺省设置下,不能匹配 “é” 或 “ç” 。 加上这 L 选项和就可以匹配了。不过这个对于中文环境似乎没有什么用,它仍然不能匹配中文字符。
M MULTILINE多行匹配。在这个模式下 ’^’( 代表字符串开头 ) 和 ’$’( 代表字符串结尾 ) 将能够匹配多行的情况,成为行首和行尾标记
S DOTALL. 将匹配所有的字符
U UNICODE\w , \W , \b , \B , \d , \D , \s 和 \S 都将使用Unicode 。
X VERBOSE这个选项忽略规则表达式中的空白,并允许使用 ’#’ 来引导一个注释。这样可以让你把规则写得更美观些。比如你可以把规则

re 模块函数

函数作用
match(rule, targetString[,flag])从字符串的开头开始匹配,如果开头位置没有匹配成功,就算失败了
search(rule, targetString[,flag])失败后跳过开头,继续向后寻找是否有匹配的字符串
findall(rule, target[,flag])在目标字符串中查找符合规则的字符串,返回一个列表
finditer(rule, target[,flag])返回一个迭代器
sub返回一个被替换的字符串
subn返回一个元组,第一个元素是被替换的字符串,第二个元素是一个数字,表明产生了多少次替换。
split(rule, target[,maxsplit])使用指定的正则规则在目标字符串中查找匹配的字符串,用它们作为分界,把字符串切片
escape(string)将字符串中的 non-alphanumerics 字符用反义字符的形式显示出来。

Pattern 对象的其他方法

方法作用
flags查询编译时的选项
pattern查询编译时的规则
groupindex规则里的组

Match 对象的方法

方法说明
group([index|id])获取匹配的组,缺省返回组 0, 也就是全部值
groups()返回全部的组
groupdict()返回以组名为 key ,匹配的内容为 values 的字典
start( [group] )获取匹配的组的开始位置
end( [group] )获取匹配的组的结束位置
span( [group] )获取匹配的组的(开始,结束)位置
expand( template )根据一个模版用找到的内容替换模版里的相应位置

Match 对象的其他属性

属性说明
pos搜索开始的位置参数
endpos搜索结束的位置参数
lastindex最后匹配的组的序号
lastgroup最后匹配的组名
re产生这个匹配的 Pattern 对象,可以认为是个逆引用
string匹配的目标字符串

导入模块

import re

基本方法

方法说明
match从开始位置开始匹配,如果开头没有则无
search搜索整个字符串
findall搜索整个字符串,返回一个list

二、详细说明

1、基本内容

1.1 基本规则

  1. [ ] 字符集合设定符
    

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

在 [ ]’中还可以通过 ‘-’ 减号来指定一个字符集合的范围.比如可以用 [a-zA-Z] 来指定所有英文字母的大小写(顺序不能颠倒)

如果在 [ ] 里面的开头写一个 ‘^’ 号,则表示取非,即在括号里的字符都不匹配。如 [^a-zA-Z] 表明不匹配所有英文字母。

如果 ‘^’ 不在开头,则它就不再是表示取非,而表示其本身。如 [a-z^A-Z] 表明匹配所有的英文字母和字符 ’^’ 。

  1. | 或规则
    

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

注意 :关于 ’|’ 要注意两点:

第一, 它在 [ ] 之中不再表示或,而表示他本身的字符。如果要在 [ ] 外面表示一个 ’|’ 字符,必须用反斜杠引导,即 ’\|’ ;
第二, 它的有效范围是它两边的整条规则,比如‘ 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’

>>> s = 'I have a dog , I have a cat'
>>> re.findall(r'I have a (?:dog|cat)', s)
['I have a dog', 'I have a cat']                # 正如我们所要的

下面再看看不用无捕获组会是什么后果:

>>> re.findall( r'I have a dog|cat', s)
['I have a dog', 'cat']       # 它将 'I have a dog' 和 'cat' 当成两个规则了

后面将仔细说明无捕获组的使用.

  1. .  匹配所有字符
    

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

   例:
>>> s='123 /n456 /n789'
>>> findall(r'.+', s)
['123', '456', '789']
>>> re.findall(r'.+', s, re.S)
['123/n456/n789']
  1. ^ 、 $   匹配字符串开头和结尾
    

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

  1. \d 匹配数字
    

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

  1. \D 匹配非数字
    

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

  1. \w 匹配字母和数字
    

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

  1. \W 匹配非英文字母和数字
    

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

  1. \s 匹配间隔符
    

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

  1. \S 匹配非间隔符
    

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

  1. \A 匹配字符串开头
    

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

  1. \Z 匹配字符串结尾
    

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

>>> s = '12 34\n56 78\n90'
>>> re.findall(r'^\d+', s, re.M)    # 匹配位于行首的数字
['12', '56', '90']
>>> re.findall(r'\A\d+', s, re.M)   # 匹配位于字符串开头的数字
['12']
>>> re.findall(r'\d+$', s, re.M)    # 匹配位于行尾的数字
['34', '78', '90']
>>> re.findall(r'\d+\Z', s, re.M)   # 匹配位于字符串尾的数字
['90']
  1. \b 匹配单词边界
    

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

例:

>>> s = 'abc abcde bc bcd'
>>> re.findall(r'\bbc\b', s)         # 匹配一个单独的单词 'bc' ,而当它是其它单词的一部分的时候不匹配
['bc']                               # 只找到了那个单独的 'bc'
>>> re.findall(r'\sbc\s', s)         # 匹配一个单独的单词 'bc'
[' bc ']                             # 只找到那个单独的 ' bc ' ,不过注意前后有两个空格
  1. \B 匹配非边界
    

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

例:

>>> s = 'abc abcde bc bcd'
>>> re.findall(r'\Bbc\w+', s)     # 匹配包含 'bc' 但不以 'bc' 为开头的单词
['bcde']                          # 成功匹配了 'abcde' 中的 'bcde' ,而没有匹配 'bcd'
  1. (?:) 无捕获组
    

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

例:匹配字符串中重复的 ’ab’

>>> s = 'ababab abbabb aabaab'
>>> re.findall(r'\b(?:ab)+\b', s)
['ababab']
如果仅使用一对括号,看看会是什么结果:
>>> re.findall(r'\b(ab)+\b', s)
['ab']

这是因为如果只使用一对括号,那么这就成为了一个组 (group) 。组的使用比较复杂,将在后面详细讲解。

  1. (?# ) 注释
    

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

  1. (?iLmsux)  编译选项指定
    

Python 的正则式可以指定一些选项,这个选项可以写在 findall 或 compile 的参数中,也可以写在正则式里,成为正则式的一部分。这在某些情况下会便利一些。具体的选项含义请看后面的 compile 函数的说明。

此处编译选项: i 等价于 IGNORECASE ,L 等价于 LOCAL ,m 等价于 MULTILINE , s 等价于 DOTALL , u 等价于 UNICODE , x 等价于 VERBOSE 。

请注意它们的大小写。在使用时可以只指定一部分,比如只指定忽略大小写,可写为 ‘(?i)’ ,要同时忽略大小写并使用多行模式,可以写为 ‘(?im)’ 。

另外要注意选项的有效范围是整条规则,即写在规则的任何地方,选项都会对全部整条正则式有效。

例:

>>> s = 'ababab \nabbabb \naabaab'
>>> re.findall(r'(?im)A', s)
['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']

1.2 重复

  1. *   0次或多次匹配
    

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

  1. +   1次或多次匹配
    

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

例:匹配以下字符串中的前一部分是字母,后一部分是数字或没有的变量名字

>>> s = ' aaa bbb111 cc22cc 33dd '
>>> re.findall(r'\b[a-z]+\d*\b', s)      # 必须至少1个字母开头,以连续数字结尾或没有数字
['aaa', 'bbb111']

注意上例中规则前后加了表示单词边界的 ’/b’ 指示符,如果不加的话结果就会变成:

>>> re.findall(r'[a-z]+\d*', s)
['aaa', 'bbb111', 'cc22', 'cc', 'dd']    # 把单词给拆开了
  1. ?   0次或1次匹配
    

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

例,匹配一个数字,这个数字可以是一个整数,也可以是一个科学计数法记录的数字,比如 123 和 10e3 都是正确的数字。

>>> s = ' 123 10e3 20e4e4 30ee5 '
>>> re.findall(r'\b\d+[eE]?\d*\b', s)
['123', '10e3']

它正确匹配了 123 和 10e3, 正是我们期望的。注意前后的 ’\b’ 的使用,否则将得到不期望的结果。

1.2.1 精确匹配和最小匹配

Python 正则式还可以精确指定匹配的次数。指定的方式是:

'{m}'     精确匹配 m 次
'{m,n}'   匹配最少 m 次,最多 n 次 (n>m)

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

例 寻找下面字符串中

a : 3 位数
b: 2 位数到 4 位数
c: 5 位数以上的数
d: 4 位数以下的数

>>> s= ' 1 22 333 4444 55555 666666 '
>>> re.findall(r'\b\d{3}\b', s)           # a : 3 位数
['333']
>>> re.findall(r'\b\d{2,4}\b', s)         # b: 2 位数到 4 位数
['22', '333', '4444']
>>> re.findall(r'\b\d{5,}\b', s)           # c: 5 位数以上的数
['55555', '666666']
>>> re.findall(r'\b\d{1,4}\b', s)         # 4 位数以下的数
['1', '22', '333', '4444']
  • *?  +?  ??  最小匹配
    

‘*’ ‘+’ ‘?’ 通常都是尽可能多的匹配字符。有时候我们希望它尽可能少的匹配。比如一个 c 语言的注释 ‘/* part 1 */ /* part 2 */’ ,如果使用最大规则:

>>> s = r'/* part 1 */ code /* part 2 */'
>>> re.findall(r'//*.*/*/', s)
['/* part 1 */ code /* part 2 */']

结果把整个字符串都包括进去了。如果把规则改写成

>>> re.findall(r'//*.*?/*/', s)     # 在 * 后面加上 ? ,表示尽可能少的匹配
['/* part 1 */', '/* part 2 */']

结果正确的匹配出了注释里的内容

1.3 前向界定与后向界定

有时候需要匹配一个跟在特定内容后面的或者在特定内容前面的字符串, Python 提供一个简便的前向界定和后向界定功能,或者叫前导指定和跟从指定功能。它们是:

  1. (?<=…)   前向界定
    

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

  1. (?=…)   后向界定
    

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

例: 你希望找出 c 语言的注释中的内容,它们是包含在 ’/*’ 和 ’*/’ 之间,不过你并不希望匹配的结果把 ’/*’ 和 ’*/’ 也包括进来,那么你可以这样用:

>>> s = r'/* comment 1 */  code  /* comment 2 */'
>>> re.findall(r'(?<=/\*).+?(?=\*/)', s)
[' comment 1 ', ' comment 2 ']

注意这里我们仍然使用了最小匹配,以避免把整个字符串给匹配进去了。

要注意的是,前向界定括号中的表达式必须是常值,不能在前向界定的括号里写正则式。比如你如果在下面的字符串中想找到被字母夹在中间的数字,你不可以用前向界定:

例:

>>> s = 'aaa111aaa , bbb222 , 333ccc '
>>> re.findall( r'(?<=[a-z]+)\d+(?=[a-z]+)', s)          # 错误的用法

它会给出一个错误信息:

error: look-behind requires fixed-width pattern

不过如果你只要找出后面接着有字母的数字,你可以在后向界定写正则式:

>>> re.findall(r'\d+(?=[a-z]+)', s)
['111', '333']

如果你一定要匹配包夹在字母中间的数字,你可以使用组( group )的方式

>>> re.findall(r'[a-z]+(\d+)[a-z]+', s)
['111']
  1. (?<!...)   前向非界定
    

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

  1. (?!...)    后向非界定
    

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

接上例,希望匹配后面不跟着字母的数字

>>> s = 'aaa111aaa , bbb222 , 333ccc '
>>> re.findall(r'\d+(?!\w+)', s)
['222']

注意这里我们使用了 \w 而不是像上面那样用 [a-z] ,因为如果这样写的话,结果会是:

>>> re.findall( r'\d+(?![a-z]+)', s)
['11', '222', '33']

1.4 组的基本知识

  1. (' ')       无命名组
    

最基本的组是由一对圆括号括起来的正则式。比如上面匹配包夹在字母中间的数字的例子中使用的 (\d+) ,我们再回顾一下这个例子:

>>> s = 'aaa111aaa , bbb222 , 333ccc '
>>> re.findall(r'[a-z]+(/d+)[a-z]+', s)
['111']

可以看到 findall 函数只返回了包含在 () 中的内容,而虽然前面和后面的内容都匹配成功了,却并不包含在结果中。

除了最基本的形式外,我们还可以给组起个名字,它的形式是:

  1. (?P<name>…)    命名组
    

‘(?P’ 代表这是一个 Python 的语法扩展 ’<…>’ 里面是你给这个组起的名字,比如你可以给一个全部由数字组成的组叫做 ’num’ ,它的形式就是 ’(?P<num>\d+)’ 。起了名字之后,我们就可以在后面的正则式中通过名字调用这个组,它的形式是

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

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

我们可以看更多的例子:请注意下面这个字符串各子串的特点。

>>> s = 'aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'

我们看看下面的正则式会返回什么样的结果:

>>> re.findall(r'([a-z]+)\d+([a-z]+)', s)      # 找出中间夹有数字的字母
[('aaa', 'aaa'), ('fff', 'ggg')]
>>> re.findall(r'(?P<g1>[a-z]+)\d+(?P=g1)', s) # 找出被中间夹有数字的前后同样的字母
['aaa']
>>> re.findall(r'[a-z]+(\d+)([a-z]+)', s)      # 找出字符串(前面有字母引导,中间是数字,后面是字母)中的中间数字和后面字母
[('111', 'aaa'), ('777', 'ggg')]

我们可以通过命名组的名字在后面调用已匹配的命名组,不过名字也不是必需的。

  1. \number    通过序号调用已匹配的组
    

正则式中的每个组都有一个序号,序号是按组从左到右,从 1 开始的数字,你可以通过下面的形式来调用已匹配的组

比如上面找出被中间夹有数字的前后同样的字母的例子,也可以写成:

>>> re.findall(r'([a-z]+)\d+\1', s)
['aaa']

我们再看一个例子:

>>> s='111aaa222aaa111 , 333bbb444bb33'
>>> re.findall(r'(\d+)([a-z]+)(\d+)(\2)(\1)', s)   # 找出完全对称的 数字-字母-数字-字母-数字 中的数字和字母
[('111', 'aaa', '222', 'aaa', '111')]
  1. (?(id/name)yes-pattern|no-pattern)   判断指定组是否已匹配,执行相应的规则
    

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

举个例子,比如要匹配一些形如 usr@mail 的邮箱地址,不过有的写成 < usr@mail > 即用一对 <> 括起来,有的则没有,要匹配这两种情况,可以这样写

>>> s='<usr1@mail1>  usr2@maill2'
>>> re.findall(r'(<)?\s*(\w+@\w+)\s*(?(1)>)', s)
[('<', 'usr1@mail1'), ('', 'usr2@maill2')]

不过如果目标字符串如下

>>> s='<usr1@mail1>  usr2@maill2 <usr3@mail3   usr4@mail4>  < usr5@mail5 '

而你想得到要么由一对 <> 包围起来的一个邮件地址,要么得到一个没有被 <> 包围起来的地址,但不想得到一对 <> 中间包围的多个地址或不完整的 <> 中的地址,那么使用这个式子并不能得到你想要的结果

>>> re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , s )
[('<', 'usr1@mail1'), ('', 'usr2@maill2'), ('', 'usr3@mail3'), ('', 'usr4@mail4'), ('', 'usr5@mail5')]

它仍然找到了所有的邮件地址。

2、re 模块的基本函数

findall(rule, target[,flag])

在目标字符串中查找符合规则的字符串。

第一个参数是规则,第二个参数是目标字符串,后面还可以跟一个规则选项(选项功能将在 compile 函数的说明中详细说明)。

返回结果结果是一个列表, 中间存放的是符合规则的字符串。如果没有符合规则的字符串被找到,就返回一个空 列表。

2.1 使用 compile 加速

compile(rule[,flag])

将正则规则编译成一个 Pattern 对象,以供接下来使用。

第一个参数是规则式,第二个参数是规则选项。

返回一个 Pattern 对象

直接使用 findall ( rule , target ) 的方式来匹配字符串,一次两次没什么,如果是多次使用的话,由于正则引擎每次都要把规则解释一遍,而规则的解释又是相当费时间的,所以这样的效率就很低了。如果要多次使用同一规则来进行匹配的话,可以使用 re.compile 函数来将规则预编译,使用编译过返回的 Regular Expression Object 或叫做 Pattern 对象来进行查找。

例:

>>> s='111,222,aaa,bbb,ccc333,444ddd'
>>> rule=r'\b\d+\b'
>>> compiled_rule=re.compile(rule)
>>> compiled_rule.findall(s)
['111', '222']

可见使用 compile 过的规则使用和未编译的使用很相似。 compile 函数还可以指定一些规则标志,来指定一些特殊选项。多个选项之间用 ’| ’ (位或)连接起来。

  1. I     IGNORECASE  忽略大小写区别
    
  2. L     LOCAL   字符集本地化
    

这个功能是为了支持多语言版本的字符集使用环境的,比如在转义符\w ,在英文环境下,它代表[a-zA-Z0-9] ,即所有英文字符和数字。如果在一个法语环境下使用,缺省设置下,不能匹配 “é” 或 “ç” 。 加上这 L 选项和就可以匹配了。不过这个对于中文环境似乎没有什么用,它仍然不能匹配中文字符。

  1. M     MULTILINE   多行匹配
    

在这个模式下 ’^’( 代表字符串开头 ) 和 ’$’( 代表字符串结尾 ) 将能够匹配多行的情况,成为行首和行尾标记。比如:

>>> s = '123 456\n789 012\n345 678'
>>> rc=re.compile(r'^\d+')    # 匹配一个位于开头的数字,没有使用 M 选项
>>> rc.findall(s)
['123']     # 结果只能找到位于第一个行首的 '123'
>>> rcm=re.compile(r'^\d+', re.M)       # 使用 M 选项
>>> rcm.findall(s)
['123', '789', '345']  # 找到了三个行首的数字

同样,对于 ’$’ 来说,没有使用 M 选项,它将匹配最后一个行尾的数字,即 ’678’ ,加上以后,就能匹配三个行尾的数字 456 012 和 678 了 .

>>> rc=re.compile(r'\d+$')
>>> rcm=re.compile(r'\d+$', re.M)
>>> rc.findall(s)
['678']
>>> rcm.findall(s)
['456', '012', '678']
  1. S     DOTALL       .   将匹配所有的字符
    

缺省情况下 ’.’ 匹配除换行符 ’\n’ 外的所有字符,使用这一选项以后, ’.’ 就能匹配包括 ’/n’ 的任何字符了。

  1. U     UNICODE      
    

\w , \W , \b , \B , \d , \D , \s 和 \S 都将使用Unicode 。

  1. X     VERBOSE      
    

这个选项忽略规则表达式中的空白,并允许使用 ’#’ 来引导一个注释。这样可以让你把规则写得更美观些。比如你可以把规则

>>> rc = re.compile(r'\d+|[a-zA-Z]+')

匹配一个数字或者单词

使用 X 选项写成:

>>> rc = re.compile(r'''
#number
\d+
#word
|
[a-zA-Z]+
''', re.VERBOSE)

在这个模式下,如果你想匹配一个空格,你必须用’\ ‘的形式(’\'后面跟一个空格)

2.2 match 与 search

match(rule, targetString[,flag])
search(rule, targetString[,flag])

(注: re 的 match 与 search 函数同 compile 过的 Pattern 对象的 match 与 search 函数的参数是不一样的。 Pattern 对象的 match 与 search 函数更为强大,是真正最常用的函数)

按照规则在目标字符串中进行匹配。

第一个参数是正则规则,第二个是目标字符串,第三个是选项(同 compile 函数的选项)

返回:若成功返回一个 Match 对象,失败返回一个 NoneType

所以在对匹配完的结果进行操作之前,你必需先判断一下是否匹配成功了,比如:

>>> m = re.match(rule, target)
>>> if m:          # 必需先判断是否成功
        do something

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

关于 match 返回的 MatchObject 如果使用的问题,是 Python 正则式的精髓所在,它与组的使用密切相关。我将在下一部分详细讲解,这里只举个最简单的例子:

例:

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

2.3 finditer

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 对象。这在对每次返回的对象进行比较复杂的操作时比较有用。

2.4 字符串的替换和修改

re 模块还提供了对字符串的替换和修改函数,他们比字符串对象提供的函数功能要强大一些。这几个函数是

  1. sub (rule, replace, target[,count])
    
  2. subn(rule, replace, target[,count])
    

在目标字符串中规格规则查找匹配的字符串,再把它们替换成指定的字符串。你可以指定一个最多替换次数,否则将替换所有的匹配到的字符串。

第一个参数是正则规则,第二个参数是指定的用来替换的字符串,第三个参数是目标字符串,第四个参数是最多替换次数。

这两个函数的唯一区别是返回值。

sub 返回一个被替换的字符串
subn 返回一个元组,第一个元素是被替换的字符串,第二个元素是一个数字,表明产生了多少次替换。

例,将下面字符串中的 ’dog’ 全部替换成 ’cat’

>>> s=' I have a dog , you have a dog , he have a dog '
>>> re.sub(r'dog', 'cat', s)
' I have a cat , you have a cat , he have a cat '

如果我们只想替换前面两个,则

>>> re.sub(r'dog', 'cat', s, 2)
' I have a cat , you have a cat , he have a dog '

或者我们想知道发生了多少次替换,则可以使用 subn

>>> re.subn(r'dog', 'cat', s)
(' I have a cat , you have a cat , he have a cat ', 3)
  1. 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 ']

结果很好。如果使用 str 对象的 split 函数,则由于我们不知道 ’,’ 两边会有多少个空格,而不得不对结果再进行一次处理。

  1. escape(string)
    

这是个功能比较古怪的函数,它的作用是将字符串中的 non-alphanumerics 字符用反义字符的形式显示出来。有时候你可能希望在正则式中匹配一个字符串,不过里面含有很多 re 使用的符号,你要一个一个的修改写法实在有点麻烦,你可以使用这个函数

例,在目标字符串 s 中匹配 ’(*+?)’ 这个子字符串

>>> s= '111 222 (*+?) 333'
>>> rule = re.escape(r'(*+?)')
>>> print(rule)
/(/*/+/?/)
>>> re.findall(rule, s)
['(*+?)']

3、更深入的了解 re 的组与对象

前面对 Python 正则式的组进行了一些简单的介绍,由于还没有介绍到 match 对象,而组又是和 match 对象密切相关的,所以必须将它们结合起来介绍才能充分地说明它们的用途。

不过再详细介绍它们之前,我觉得有必要先介绍一下将规则编译后的生成的 patter 对象

3.1 编译后的 Pattern 对象

将一个正则式,使用 compile 函数编译,不仅是为了提高匹配的速度,同时还能使用一些附加的功能。编译后的结果生成一个 Pattern 对象,这个对象里面有很多函数,他们看起来和 re 模块的函数非常象,它同样有 findall , match , search ,finditer , sub , subn , split 这些函数,只不过它们的参数有些小小的不同。一般说来, re 模块函数的第一个参数,即正则规则不再需要了,因为规则就包含在 Pattern 对象中了,编译选项也不再需要了,因为已经被编译过了。因此 re 模块中函数的这两个参数的位置,就被后面的参数取代了。

findall , match , search 和 finditer 这几个函数的参数是一样的,除了少了规则和选项两个参数外,它们又加入了另外两个参数,它们是:查找开始位置和查找结束位置,也就是说,现在你可以指定查找的区间,除去你不感兴趣的区间。它们现在的参数形式是:

findall(targetString[,startPos[,endPos]])
finditer(targetString[,startPos[,endPos]])
match(targetString[,startPos[,endPos]])
search(targetString[,startPos[,endPos]])

这些函数的使用和 re 模块的同名函数使用完全一样。所以就不多介绍了。

除了和 re 模块的函数同样的函数外, Pattern 对象还多了些东西,它们是:

方法作用
flags查询编译时的选项
pattern查询编译时的规则
groupindex规则里的组

这几个不是函数,而是一个值。它们提供你一些规则的信息。比如下面这个例子

>>> p = re.compile(r'(?P<word>\b[a-z]+\b)|(?P<num>\b\d+\b)|(?P<id>\b[a-z_]+\w*\b)', re.I)
>>> p.flags
34
>>> p.pattern
'(?P<word>\b[a-z]+\b)|(?P<num>\b\d+\b)|(?P<id>\b[a-z_]+\w*\b)'
>>> p.groupindex
{'word': 1, 'num': 2, 'id': 3}

我们来分析一下这个例子:这个正则式是匹配单词、或数字、或一个由字母或 ’_’ 开头,后面接字母或数字的一个 ID 。我们给这三种情况的规则都包入了一个命名组,分别命名为 ’word’ , ‘num’ 和 ‘id’ 。我们规定大小写不敏感,所以使用了编译选项 ‘I’ 。

编译以后返回的对象为 p ,通过 p.flag 我们可以查看编译时的选项,不过它显示的不是 ’I’ ,而是一个数值 2 。其实 re.I 是一个整数, 2 就是它的值。我们可以查看一下:

>>> re.I
<RegexFlag.IGNORECASE: 2>
>>> re.L
<RegexFlag.LOCALE: 4>
>>> re.M
<RegexFlag.MULTILINE: 8>

每个选项都是一个数值。

通过 p.pattern 可以查看被编译的规则是什么。使用 print 的话会更好看一些

>>> print(p.pattern)
'(?P<word>\b[a-z]+\b)|(?P<num>\b\d+\b)|(?P<id>\b[a-z_]+\w*\b)'

和我们输入的一样。

接下来的 p.groupindex 则是一个字典,它包含了规则中的所有命名组。字典的 key 是名字, values 是组的序号。由于字典是以名字作为 key ,所以一个无命名的组不会出现在这里。

3.2 组与 Match 对象

组与 Match 对象是 Python 正则式的重点。只有掌握了组和 Match 对象的使用,才算是真正学会了 Python 正则式。

3.2.1 组的名字与序号

正则式中的每个组都有一个序号,它是按定义时从左到右的顺序从 1 开始编号的。其实, re 的正则式还有一个 0 号组,它就是整个正则式本身。

我们来看个例子

>>> p = re.compile(r'(?P<name>[a-z]+)\s+(?P<age>\d+)\s+(?P<tel>\d+).*', re.I)
>>> p.groupindex
{'age': 2, 'tel': 3, 'name': 1}
>>> s = 'Tom 24 88888888  <='
>>> m = p.search(s)
>>> m.groups()                        # 看看匹配的各组的情况
('Tom', '24', '8888888')
>>> m.group('name')                   # 使用组名获取匹配的字符串
'Tom'
>>> m.group(1)                      # 使用组序号获取匹配的字符串,同使用组名的效果一样
>>> m.group(0)                        # 0 组里面是什么呢?
'Tom 24 88888888  <='

原来 0 组就是整个正则式 , 包括没有被包围到组里面的内容。当获取 0 组的时候,你可以不写这个参数。 m.group(0) 和 m.group() 的效果是一样的:

>>> m.group()
'Tom 24 88888888  <='

接下来看看更多的 Match 对象的方法,看看我们能做些什么。

3.2.2 Match 对象的方法
方法说明
group([index|id])获取匹配的组,缺省返回组 0, 也就是全部值
groups()返回全部的组
groupdict()返回以组名为 key ,匹配的内容为 values 的字典

接上例:

>>> m.groupindex()
{'age': '24', 'tel': '88888888', 'name': 'Tom'}
方法说明
start( [group] )获取匹配的组的开始位置
end( [group] )获取匹配的组的结束位置
span( [group] )获取匹配的组的(开始,结束)位置
expand( template )根据一个模版用找到的内容替换模版里的相应位置

这个功能比较有趣,它根据一个模版来用匹配到的内容替换模版中的相应位置,组成一个新的字符串返回。它使用 \g<index|name> 或 \index 来指示一个组。

接上例

>>> m.expand(r'name is /g<1> , age is /g<age> , tel is /3')
'name is Tom , age is 24 , tel is 88888888'

除了以上这些函数外, Match 对象还有些属性

属性说明
pos搜索开始的位置参数
endpos搜索结束的位置参数

这两个是使用 findall 或 match 等函数时,传入的参数。在上面这个例子里,我们没有指定开始和结束位置,那么缺省的开始位置就是 0, 结束位置就是最后。

>>> m.pos
0
>>> m.endpos
19
属性说明
lastindex最后匹配的组的序号
>>> m.lastindex
3
属性说明
lastgroup最后匹配的组名
>>> m.lastgroup
'tel'
属性说明
re产生这个匹配的 Pattern 对象,可以认为是个逆引用
>>> m.re.pattern
'(?P<name>[a-z]+)\s+(?P<age>d+)s+(?P<tel>d+).*'

得到了产生这个匹配的规则

属性说明
string匹配的目标字符串
>>> m.string
'Tom 24 88888888  <='
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值