转载:Python之正则表达式

(本文大部分内容来源于此链接的文章,我对部分内容做了一些改动,方便自己日后复习,若有侵权,请联系我,我会将此文删除,谢谢)


import re  # 导入正则表达式模块

re.match()  # 功能是,从开始位置匹配,若开头不匹配,则返回None
re.search() # 在整个字符串搜索匹配
re.findall() # 在整个字符串搜索匹配,返回值为list

注意,r’pattern’的r表示原生字符(英文为raw),表示不会对pattern字符串中的所有字符进行任何转义。

正则表达式中,可使用可选标志修饰符来控制匹配模式。想使用多个标志可通过|来指定,如,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 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

序号 部分代码展示
1 re.search(r’[a-z]+’, ‘liuyaN1234ab9’).group() # 返回’liuya’
2 re.search(r’[a-z]+’, ‘liuyaN1234ab9’, re.I).group() #返回’liuyaN’,因为指定了re.I,即对大小写不敏感,大写小写都可以匹配成功
3 if re.match(r’[0-9]’, ‘123a’): print(‘Yeah!’) # 匹配成功,返回值Yeah!
4 re.split(r’\s+’, ‘a b c d’) # 用空格分割,返回值为[‘a’, ‘b’, ‘c’, ‘d’]
5 re.split(r’[\s\,]+’, ‘a, b c, d’) # 返回值为[‘a’, ‘b’, ‘c’, ‘d’]
6 re.match(r’[0-9]’, ‘3’).group() # 返回值为’3’

模式字符串使用特殊语法来表示一个正则表达式:

  1. 字母与数字表示自己本身,一个正则表达式中的字母和数字匹配相同的字符串。
  2. 标点符号只有被转义时才匹配自身,否则表示特殊含义。
  3. 由于大部分的正则表达式都包含反斜杠\,故最好使用原始字符串来表示,即r’string’。

下表为正则表达式语法中的特殊元素:

模式 描述
^ 匹配字符串的开头
$ 匹配字符串的末尾
. 匹配除换行符外的任意字符
[…] 用来表示一组字符,如,[abc]匹配’a’, ‘b’, ‘c’
[^…] 匹配不在[]中的字符,如[^abc]匹配除a,b,c之外的字符
re* 匹配0个或多个的表达式
re+ 匹配1个或多个的表达式
re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{n} 精确匹配n个前面的表达式
re{n,} 匹配前面的表达式至少n次
re{n,m} 匹配前面的表达式至少n次,最多m次
a|b 匹配a或b
\w 匹配字母数字下划线
\W 匹配非字母数字下划线的字符
\s 匹配任意空白字符,等价于[\t\n\r\f]
\S 匹配任意非空字符
\d 匹配任意数字,等价于[0-9]
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束。若存在换行,只匹配到换行前的结束字符串
\z 匹配字符串结束
\G 匹配最后匹配完成的位置
\b 匹配一个单词边界,即单词与空格间的位置,如,'er\b’可以匹配‘never’的‘er’,但不能匹配‘verb’的‘er’
\B 匹配非单词边界,如,'er\B’可以匹配‘verb’的‘er’,但不能匹配‘never’的‘er’

正则表达式实例

1 字符匹配

正则表达式 功能
python 匹配python

2 字符类

正则表达式 功能
[Pp]ython 匹配’Python’或’python’
[aeiou] 匹配aeiou中的任意字母
[0-9] 匹配任意数字
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任意数字及字母
[^aeiou] 匹配除aeiou外的任意字母
[^0-9] 匹配除数字外的字符

3 特殊字符类

正则表达式 功能
. 匹配除‘\n’之外的任意单个字符。若要匹配包括‘\n’在内的任何字符,可用‘[.\n]’的模式
\d 匹配一个数字字符。等价于[0-9]
\D 匹配一个非数字字符。等价于[^0-9]
\s 匹配任何空白字符,等价于[\f\n\r\t\v]
\S 匹配任何非空白字符,等价于[^\f\n\r\t\v]
\w 匹配包括下划线的任意单词字符,等价于’[A-Za-z0-9_]’。
\W 匹配任意非单词字符,等价于’[^A-Za-z0-9_]’。

Python的正则表达式的模块是re。下面为一段示例代码,作用是在字符串s中查找子字符串’abc’:

import re

s = '123abc456abc789abc'
re.findall(r'abc', s)

运行结果为:

['abc', 'abc', 'abc']

上面用到的函数findall(rule, target[,flag])的作用是在目标字符串中查找符合规则的子字符串。rule: 匹配规则(即正则表达式)。target: 目标字符串(在这个字符串中查找,最后找到匹配成功的子字符串),flag:代表规则选项参数,是可选的。返回值为一个列表,元素是符合规则的字符串。若匹配失败,返回空列表。


基本规则:

1 字符集合设定符——[]

由方括号括起来的字符,表示一个字符集合,检验待匹配字符串中是否包含这个字符集合中的任意一个字符。如,正则表达式[abc]表示字符’a’、‘b’、'c’都符合这个正则表达式的要求。

方括号中还能使用’-'号指定一个字符集合的范围,如,正则表达式[a-zA-Z]代表所有英文字母的大小写。

方括号中的开头若有’^‘号,则表示取非。如,正则表达式[^a-zA-Z]表明匹配除英文字母外的所有字符。注意,若’^‘号不在开头,则不再表示取非,而是只表示本身,如[a-z^A-Z]表示匹配所有英文字母和’^'字符。

2 或’|'

将多个规则用’|'连接起来,表示只要满足其一即可匹配。

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

示例代码如下:

import re

s = 'a dog, a cat'
re.findall(r'a (?:dog|cat)', s)

输出结果为:

['a dog', 'a cat']

下面的代码中,re.findall为使用无捕获组(?: )

import re

s = 'a dog, a cat'
re.findall(r'a dog|cat', s)

输出结果为:

['a dog', 'cat']

从上述结果中可以看出findall函数将’a dog’ 和 ‘cat’分开,当成两个规则了,而不是将其视为’a dog’ 和’a cat’了。(无捕获组将在后面说明)

3 匹配所有字符.

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

下面为实例代码:

import re

s = '123\n456\n789'
re.findall(r'.+', s)  # 1
re.findall(r'.+', s, re.S)  # 2

输出结果为:

['123', '456', '789'] # 1,不可以匹配换行符
['123\n456\n789'] # 2,指定了re.S,可以匹配换行符

4 匹配字符串开头^ 结尾$

注意,^在匹配开头时不能被包含在[]中,否则含义会变为匹配不在中括号[]内的字符。在多行模式下,^$可以匹配每一行的行首尾部(多行模式什么的,后面会在函数compile的部分讲解)。

5 匹配字符串开头\A 与结尾\Z

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

\Z$的区别是,前者只匹配整个字符串的结尾,即使在’re.M’模式下,也不会匹配其他行的行尾。

示例代码如下:

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) # 匹配位于最后一行行尾的数字

输出结果为:

['12', '56', '90']
['12']
['34', '78', '90']
['90']

6 匹配单词边界\b和非边界\B

\b匹配单词边界,是一个零长度字符,匹配完的字符串不会包含分界符。\s匹配时,匹配完的字符串会包含分界符。

实例代码如下:

import re

s = 'abc abcde bc bcd'
re.findall(r'\bbc\b', s) # 匹配单词'bc',而当'bc'为其他单词的一部分时不能匹配
re.findall(r'\sbc\s', s) # 匹配单词'bc',前后还有空白符

输出结果为:

['bc']
[' bc ']  # 相较于上一结果,'bc'左右都有一个空格

\B匹配的是非边界字符。是零长度字符。

实例代码如下:

import re

s = 'abc abcde bc bcd'
re.findall(r'\Bbc\w+', s)  # 匹配包含bc,但是不以bc开头的单词

输出结果为:

['bcde']  # 匹配的是abcde的bcde

7 无捕获组(?:)

当想将规则作为一个整体对其进行操作,如,指定其重复次数时,我们需要将这部分规则用(?:)包起来,而不能仅仅使用一对括号。

下面的代码是匹配字符串中重复的ab:

import re

s = 'ababab abbabb aabaab'
re.findall(r'\b(?:ab)+\b', s)

输出结果为:

['ababab']

8 注释(?#)

当在(?#)之间填入内容时,python会把内容当成注释,因而会在匹配时忽略这些内容。

9 编译选项指定(?iLmsux)

Python的正则表达式能指定一些选项,这些选项可以写在函数findall或compile的参数中,也可以写在正则表达式中。

1、i是忽略大小写。

2、L是表示特殊字符集(\w, \W, \b, \B, \s, \S),依赖于当前环境。

3、m是多行模式。

4、s是.并且包含换行符在内的任意字符。

5、u是表示特殊字符集(\w, \W, \b, \B, \s, \S, \d, \D),依赖于Unicode字符属性数据库。

6、x是为了增加可读性,忽略空格和#后面的注释。


重复

Python中重复规则的一般形式是,在一条字符规则后紧跟一个表示重复次数的规则,来表示需要重复前面的规则多少次。

具体的规则如下:

1 0次或多次匹配*

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

2 1次或多次匹配+

+表示匹配前面的规则至少一次。

示例代码如下:

import re

s = 'aaa bbb111 cc22cc 33dd'
re.findall(r'\b[a-z]+\d*\b', s) # 匹配“至少以一个字母开头,且以连续数字结尾或没有数字的字符串片段

输出结果为:

['aaa', 'bbb111']

下面的代码与上述代码的不同之处是,下面的代码中正则表达式的两边未添加\b指示符:

import re

s = 'aaa bbb111 cc22cc 33dd'
re.findall(r'[a-z]+\d*', s)

输出结果是:

['aaa', 'bbb111', 'cc22', 'cc', 'dd']

上述结果表明,这样的正则表达式会将单词拆开,而在多数情况下,这不是我们期望的结果。

3 0次或1次匹配?

?表示匹配前面的规则0或1次。下面的代码可以匹配一个数字,这个数字的类型是整数或是科学计数法记录的数字,如123与10e3都是可以匹配的数字:

import re

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

输出结果为:

['123', '10e3']

上述结果符合预期。注意:正则表达式的开头与结尾需包含\b,否则,得到的结果将会变得出乎意料。

4 精确匹配和最小匹配

Python正则表达式还能指定匹配的次数,如下:

‘{m}’ 精确匹配m次

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

‘{m,}’ 匹配至少m次

示例代码如下:

import re

s = '1 22 333 4444 55555 666666'

re.findall(r'\b\d{3}\b', s) # 寻找s中的三位数
re.findall(r'\b\d{2,4}\b', s) # 寻找s中的二位数到四位数
re.findall(r'\b\d{5,}\b', s) # 寻找s中的五位及以上的数
re.findall(r'\b\d{1,4}\b', s) # 寻找s中的四位及以下的数

输出结果为:

['333']
['22', '333', '4444']
['55555', '666666']
['1', '22', '333', '4444']

5 最小匹配 *?+???

*+?一般都是尽可能的匹配多个字符。但是有时我们需要的是尽可能少的匹配。如,下面的代码是为了匹配C语言的注释/* part1 */ /* part2 */,使用的是最大规则:

import re

s = r'/* part1 */ code /* part2 */'
re.findall(r'\/\*.*\*\/', s) # 最大匹配规则
re.findall(r'\/\*.*?\*\/', s) # 第一和第三个*是转义的\*,分别表示/*的*和*/的*。中间的*与.搭配成.*,表示匹配任意字符(除换行符)0次或更多次,而加上?号,成为.*?表示尽可能匹配最少字符。

输出结果为:

['/* part1 */ code /* part2 */']
['/* part1 */', '/* part2 */']

从上述结果中可以看出第二个findall函数的返回结果是符合预期的,它只返回了注释行。而第一个还返回了代码部分。因此在此种情况下(指的是要求只返回注释而不包含代码时),应采取第二种正则表达式形式。


前向界定和后向界定

有时,我们需要匹配一个跟在特定内容后面的或前面的字符串。Python提供了一个简便的前向界定和后向界定功能,如下:

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

...代表希望匹配的字符串的前面应该出现的字符串。

2 后向界定(?=...)

...代表希望匹配的字符串的后面应该出现的字符串。

这是一个例子:我们希望找到C语言的注释中的内容,而注释是包含在/**/中间的,可我们又不希望匹配时把/**/也包括进来。针对这个要求,示例代码如下:

import re

s = r'/* comment1 */ code /* comment2 */'
re.findall(r'(?<=\/\*).+?(?=\*\/)', s)

输出结果为:

[' comment1 ', ' comment2 ']

注意在正则表达式中,使用了.+?最小匹配,以免把正式代码匹配进去。

注意,前向界定括号中的表达式必须是常值,也就是说,不能在前向界定的括号里写正则表达式。

示例代码如下:

s = 'aaa111aaa, bbb222, 333ccc'
re.findall(r'(?<=[a-z]+)\d+(?=[a-z]+)', s)

运行上述代码。会出现大量错误提示信息,最后一条是:

re.error: look-behind requires fixed-width pattern

上面的代码本意是为了匹配符合这样格式的数字:即数字两边都是字母时,匹配成功。但是Python要求:前向界定括号中的表达式必须是常值,即不能在前向界定的括号里写正则表达式。由此,Python解释器返回错误信息。

为了匹配【数字两边都是字母的数字】,可以这样做:

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

输出结果为:

['111']

当想找到【数字后面有字母的数字】时,可以在后向界定写正则表达式:

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

输出结果为:

['111', '333']

3 前向非界定(?<!...)

当我们希望的字符串前面不是...的内容时,才匹配这个字符串。

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

当我们希望的字符串后面不跟着...的内容时,才匹配这个字符串。

下面的代码是为了【匹配后面不跟着字母的数字】:

import re

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

输出结果为:

['222']

需要注意的是,使用的不是[a-z]而是\w,下面的代码将\w改为[a-z],让我们来看看会发生什么:

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

输出结果为:

['11', '222', '33']

有点尴尬,和预期不符。除了’222‘是符合预期的,再看看其他两个数:因为111与222中的前两个数字11和22也满足【是后面不跟着字母的数字】。(事实上我觉得还是有点绕,费点脑细胞以后才可能懂个大概)


组的基本知识

1 无命名组
最基本的组是由一对圆括号括起来的正则表达式。比如上面匹配包夹在字母中间的数字的例子中使用的(\d+),再看看这代码吧:

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

输出结果为:

['111']

显然,返回值仅仅包括(\d+)的内容。在这里,111的前后的内容(均为aaa)都匹配成功了,但是并没有包含在输出结果中。

除了最基本的形式,还能给组起名字。形式如下:

2 命名组 (?P<name>...)

(?P代表这是一个Python的语法扩展。而<name>里面的name是给这个组起的名字,如将一个全部由数字组成的组叫做num,形式为(?P<name>\d+)。在起名以后,就能在后面的正则表达式中通过名字调用这个组。调用已匹配的命名组的格式是(?P=name)。要注意,再次调用的这个组是已被匹配的组,即它的内容和命名组的内容是一样的。

示例代码如下:

import re

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

re.findall(r'([a-z]+)\d+([a-z]+)',s) # 匹配中间夹有数字的字母(只匹配字母,不匹配数字)
re.findall(r'(?P<g1>[a-z]+)\d+(?P=g1)',s) # 匹配中间夹有数字的字母,且前后字母要一致
re.findall(r'[a-z]+(\d+)([a-z]+)',s) # 先找出这种字符串:<字母><数字><字母>,然后匹配<数字><字母>

输出结果为:

[('aaa', 'aaa'), ('fff', 'ggg')]
['aaa']
[('111', 'aaa'), ('777', 'ggg')]

注意,上面代码中的返回值,都是正则表达式中括号()里的内容的匹配结果。

可通过命名组的名字在后面调用已匹配的命名组,但名字并非必需的。

可通过序号调用已匹配的组:/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')]

re 模块的基本函数

1 findall函数

语法:findall(rule, target[, flag])。
功能:在目标字符串中查找符合规则的字符串。
参数:rule为规则(正则表达式);target为目标字符串;flag是可选规则。
返回值:返回结果是列表类型的,存放的是符合规则的字符串。若未找到符合规则的字符串,即匹配失败时,返回空列表[]

2 compile函数

*语法:compile(rule[, flag])
功能:将正则表达式编译成一个Pattern对象,以供后面使用。
参数:rule为规则(正则表达式);flag是可选规则。
返回值:返回一个Pattern对象

直接使用findall()函数来匹配字符串时,如果多次使用的话,效率会很低。这是因为:正则引擎每次都需要把正则表达式解释一遍,这很耗费时间,因此这样的效率很低。若要多次使用同一规则时,可用compile函数将规则预编译,而后再使用编译过返回的pattern对象来进行匹配。

示例代码如下:

import re

s = '111,222,aaa,bbb,ccc333,444ddd'
compiled_rule = re.compile(r'\b\d+\b') # 匹配单独的数字
compiled_rule.findall(s)

输出结果为:

['111', '222']

compile函数还可以指定规则标志,多个选项之间用|(位或)连接。

I ignorance:忽略大小写。

L local:字符集本地化。这个选项是为了支持多语言版本的字符集使用环境的。如,\w在英文环境下代表[a-zA-Z0-9_],即字母数字下划线,而在法语环境下使用时,在默认设置下不能匹配éç,加上这个L选项就可以匹配了。

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

示例代码如下:

s = '123 456\n789 012\n345 678*'

rc = re.compile(r'^\d+') # 匹配位于开头的数字,未使用M选项(多行匹配)
rc.findall(s)

rcm = re.compile(r'^\d+', re.M) # 使用M选项
rcm.findall(s)

输出结果为:

['123']
['123', '789', '345']

同样,对于$,若没有M选项,正则表达式将只匹配最后一个行尾的数字,即678;加上M选项以后,将匹配三个行尾的数字456012678。示例代码如下:

s = '123 456\n789 012\n345 678'

rc = re.compile(r'\d+$')
rc.findall(s)

rcm = re.compile(r'\d+$', re.M)
rcm.findall(s)

输出结果为:

['678']
['456', '012', '678']

S dotall.默认匹配(除换行符\n外的)所有字符,使用S选项后,就可以匹配(包括换行符\n在内的)所有字符。

U unicode:\w、\W、\b、\B、\d、\D、\s、\S,都使用Unicode。(并不懂这是啥意思)

X verbose:这个选项忽略规则中的空白,并且允许使用#号来引导一个注释。使用这个选项可以让你把规则写得更加易于理解一些。

看看下面的正则式:

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

显然上面的正则表达式要求匹配数字或者单词。可以用X选项将上面的代码改为:

rc = re.compile(r"""
# 正则表达式的开始
\d+ # 数字
|# 规则'位或'
[a-zA-Z]+ # 单词
""", re.VERBOSE)

可以体会出,使用了verbose选项,且对正则表达式进行一定的编排和加注释以后,使其更易于理解。

3 match()与search()函数

语法:match(rule, targetString[, flag])
search(rule, targetString[, flag])
注意:【re的match与search函数】同【compile编译过的pattern对象的match与search函数】的参数是不一样的。后者更强大,是最常用的形式。
参数:rule为规则(正则式),targetString是目标字符串,第三个为可选选项
功能:在目标字符串中按照规则匹配得到指定格式的子字符串。
返回值:匹配成功则返回匹配对象,失败则无返回值。返回值不是一个字符串列表,而是一个MatchObject(如果匹配成功的话),通过操作这个MatchObject,可得到更多有用信息。

需要注意的是,若匹配失败,两个函数均会返回一个NoneType。所以在对匹配结果进行操作时,需事先判断是否匹配成功,如:

m = re.match(rule, targetString)

if m:
	dosomething # 需要先判断匹配成功与否

match()与research()的唯一区别是:前者从目标字符串的开头开始匹配,若未在开头位置匹配成功,则匹配失败;而后者未在开头匹配成功时,会跳过开头,继续往后寻找是否可以匹配的字符串片段。在需求不一样的情况下,根据匹配方式来选择是使用match还是research。

示例代码如下:

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'】

4 finditer()函数

语法:finditer(rule, targetString[, flag])
参数:rule为规则(正则式),targetString是目标字符串,第三个为可选选项。
返回值:返回一个迭代器。

finditer和findall的区别是:前者返回所有匹配的字符串,并存为一个列表,而后者并不会直接返回匹配成功的字符串,而是返回一个迭代器。

示例代码如下:

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()的形式,可按顺序依次得到匹配成功返回的MatchObject。这在【对每次返回的对象进行复杂操作】时非常有用。

5 sub()与subn()

语法:sub(rule, replace, targetString[, count])
subn(rule, replace, targetString[, count])
参数:rule是正则式,replace是用来将匹配的结果替换成为的字符串,targetString是目标字符串,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)

6 split()切片函数

语法:split(rule, targetString[, maxsplit])
参数:rule是正则式,targetString是目标字符串,maxsplit是最多切片次数。
功能:使用正则式在目标字符串中查找匹配的字符串,并且将这些匹配成功的字符串作为分界,将目标字符串进行切片。
返回值:返回一个【已经被切片】以后的字符串的列表。

正则模块中的split()与str对象的split()较为相似。下面的代码是想用,把字符串s分割开,同时还要求去掉逗号前后的空格:

s = 'I am fine , you are ok , he is awesome'
re.split('\s*,\s*', s)

输出结果如下:

['I am fine', 'you are ok', 'he is awesome']

上述输出符合预期想要的结果

7 escape(string)

这个函数可以将字符串中所有可能被解释为正则运算符的字符进行转义,简而言之就是将字符串中的【特殊字符(非字母数字)】转义。

示例代码如下:

s = '[a-z]+'

rule = re.escape(s)
print(rule)

输出结果为:

\[a\-z\]\+

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

1 编译后的pattern对象

用compile编译正则式,不仅是为了提高匹配速度,而且利用【编译过后生成的pattern对象】可以进行其他工作。pattern对象也有findall、match、search、finditer、sub、subn、split这些函数,区别是参数不同。通常,re模块函数的第一个参数,即正则式不再需要了,因为规则已包含在pattern对象中;也不需要编译选项,因为pattern就是由正则式编译过来的。

其中四个函数的语法规则如下:

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

参数:targetString是目标字符串,startPos为查找开始位置,endPos为查找结束位置(后面这两个参数意味着我们可以指定查找的区间,除去不感兴趣的区间)。

2 组与Match对象

组与Match对象是Python正则式的重点。

2.1 组的名字与序号

正则式的每个组都有序号,序号是按定义时从左到右的顺序从1开始编号。0号组就是正则式匹配目标字符串之后的结果。

示例代码如下:

p = re.compile(r'(?P<name>[a-z]+)\s+(?P<age>\d+)\s+(?P<tel>\d+).*', re.I)
p.groupindex # 输出【mappingproxy({'name': 1, 'age': 2, 'tel': 3})】

s = 'xw 23 123456 <++'
m = p.search(s) # 返回【<re.Match object; span=(0, 16), match='xw 23 123456 <++'>】
m.groups()  # 查看匹配的各组的情况,返回【('xw', '23', '123456')】
m.group('name') # 返回【'xw'】
m.group(1) # 使用组序号来获取匹配的字符串,返回值为【'xw'】
m.group(0) # 第0组为正则式匹配目标字符串之后的结果,返回值为【'xw 23 123456 <++'】。注意,m.group()与m.group(0)的效果是一样的

2.2 Match对象的方法

方法 描述(下面三个方法都是通过【Match对象.方法】来调用,如m.groups())
group([index|id]) 获取匹配字符串以后的对应索引的组,未指定参数时返回0组,即返回正则式匹配目标字符串之后的结果
groups() 返回全部的组,是元组类型
groupdict() 返回以组名为key,匹配的内容为values的字典

下面的代码是上一段代码的延续:

m.groupdict()  # 返回以组名为key,匹配的内容为values的字典,返回值为【{'name': 'xw', 'age': '23', 'tel': '123456'}】
方法 描述(这三个方法都是通过【Match对象.方法】来调用),如m.start(…);而括号里面的内容...,可以是【组名】或者【组的序号】
start() 获取匹配的组的开始位置
end() 获取匹配的组的结束位置
span() 获取匹配的组的开始与结束位置

【expand(template)】,这也是一个Match对象的方法,是用正则表达式匹配目标字符串成功后的结果去替换模板里对应的位置,组成一个新的字符串后返回它。在template中使用\g<index|name>\index表示一个组。

下面的代码还是上一段代码的延续(苦笑):

m.expand(r'name is \g<1>, age is \g<age>, tel is \3') # 返回值为【'name is xw, age is 23, tel is 123456'】

Match对象还有这些属性:

  • pos:搜索开始的位置参数
  • endpos:搜索结束的位置参数
  • lastindex:匹配的最后一个组的序号
  • lastgroup:匹配的最后一个组的组名
  • string:匹配的目标字符串(targetString)

示例代码如下:

m.pos # 在上面的代码中未指定开始和结束位置,所以缺省的开始位置是0,即返回【0】
m.endpos # 缺省的结束位置是16,即返回【16】
m.lastindex # 共3个组,最后一个组的序号自然是【3】
m.lastgroup # 匹配的最后一个组的组名未【'tel'】
m.string # 返回【'xw 23 123456 <++'】

(本文大部分内容来源于此链接的文章,我对部分内容做了一些改动,方便自己日后复习,若有侵权,请联系我,我会将此文删除,谢谢)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章