7.re模块
re模块是Python的内置模块,无需安装。
re模块的调用方式:import re。
re库默认采用贪婪匹配,即输出匹配最长的子串。
模式和被搜索的字符串既可以是Unicode字符串(str),也可以是8位字节串(bytes)。但是两者不能混用,即不能用一个字节串模式去匹配Unicode字符串,反之亦然。替换操作也是一样的,替换字符串的类型、模式、搜索字符串的类型必须一致。
正则表达式使用反斜杠字符\
来进行转义,使得某些特殊字符能够表示其本身的含义。比如.
表示匹配除换行符以外的任意字符,\.
则表示.
本身。
而Python中也会使用反斜杠字符来进行转义,这就可能造成困扰。比如要匹配\
,正则表达式可以表示成\\
,这里使用反斜杠转移。而在Python里每个反斜杠又必须表示成\\
,因此最终的模式字符串就是\\\\
。这未免过于复杂。
解决办法是使用Python的原生字符串表示法,在字符串前加'r'。原生字符串中的每个字符都表示它本身的含义。因此r"\n"
表示\
和n
两个字符的字符串,而"\n"
则表示一个换行符的字符串。上述反斜杠的模式就可以表示成r"\\"
。
一、常用函数说明
(1)re.search(pattern, string, flags=0)
re.search()
在一个字符串中搜索匹配正则表达式的第一个位置。
如果函数匹配成功则会返回一个匹配对象(Match对象,后面会详细说明),否则返回None。
参数说明:pattern是匹配的正则表达式。string是要匹配的字符串。flags是正则表达式使用时的控制标记,后面会详细说明。
函数的用途:可以用来进行数据验证,在正则表达式两端加上\A
和\Z
,并判断返回值是否为None
例1:验证字符串是否为6位数字
例2:pep20.txt存储了网页https://www.python.org/dev/peps/pep-0020/的内容,从中提取出网页的标题。标题的格式形如<title>标题内容</title>
先读取文本内容
用正则表达式来匹配
group()
方法是取出分组匹配结果,默认为0,表示取出整个匹配结果。这里将标签title及其内容都取出来了,如果我们只想要内容怎么办呢?可以使用子表达式来捕获,然后取出来即可:
这里取出第一个分组的结果,也就是标题内容。
作业1:验证输入的文本是否为合法的日期格式。规定合法的日期格式为"yyyy-mm-dd"。月份和日期可以不规范写,例如2020-12-2和2020-2-12也是合法的。不考虑日期是否真实存在。要求表达式如果合法返回True,否则返回False。
答案:
作业2:从pep20.txt里取出Python之禅的内容,即:
答案:
(2)re.match(pattern, string, flags=0)
re.match()
从一个字符串的起始位置匹配正则表达式,如果不是起始位置匹配成功的话就返回None
参数和re.search
的参数一样。
注意即便是 MULTILINE
多行模式(多行模式下^
会将给定字符串的每行当作匹配开始), re.match()
也只匹配字符串的开始位置,而不匹配每行开始。
这个方法可以用来筛选出我们需要的数据。
例如我们想从pep20.txt内容里找出下面这个表格里的内容(见PEP的页面https://www.python.org/dev/peps/pep-0020/):
Title:
The Zen of Python
Author:
tim.peters at gmail.com (Tim Peters)
Status:
Active
Type:
Informational
Created:
19-Aug-2004
Post-History:
22-Aug-2004
可以发现上面的内容在一个表格里,相关的html文本是:
我们需要的内容其实都包含在<tr class="field"><th class="field-name">内容1</th><td class="field-body">内容2</td>
这样的形式中,且都以换行符分隔。因此在读取文件的时候,可以按行读取,验证每一行是否是我们需要的内容:
输出结果:
(3)re.fullmatch(pattern, string, flags=0)
re.fullmatch()
如果整个字符串匹配到正则表达式,就返回一个相应的匹配对象,否则返回None。
这个方法要求字符串和正则表达式完全匹配,如果只是部分匹配则返回None
re.match()
中提取表格内容的例子也可以用re.fullmatch()
实现,但是每一行要去掉前后的空格。
(4)re.split(pattern, string, maxsplit=0, flags=0)
re.split()
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型。
maxsplit是分隔次数,例如maxsplit=1表示分隔一次,默认为 0,表示不限制次数。 如果是负数,表示不做任何切分。
例如:
加上()会使得分割后的列表保留分隔符,不加的话就不会保留。
如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样
样式的空匹配将分开字符串,但只在不相临的状况生效。
作业:以数字和空格(即空格和数字都必须有)作为分隔符分割这句话"我有1只松鼠 2条狗 66只猫 还有鱼7 信不信",使得分割后结果为['我有1只松鼠', '2条狗', '66只猫 还有鱼7', '信不信']
答案:
看到结果是:有空格+数字,或者数字+空格的地方都被分隔开了。且分隔后将空格去掉,数字保留。
(?<=\d)\s+
是查找数字后面的空格(可以是多个),\s+(?=\d)
是查找数字前面的空格(可以是多个)。
(5)re.findall(pattern, string, flags=0)
re.findall()
搜索字符串,以列表类型返回全部不重复的能匹配的子串。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。
例如寻找文本中出现的所有英文字母:
作业1:还是以取出PEP网页的表格内容为例,如何用re.findall()
实现呢?
答案:不需要按行读取文本,一行代码搞定:
输出一个列表:
作业2:取出PEP网页中所有形如title="xxxx"
里的xxxx。注意使用非贪婪匹配。并且过滤xxxx为空的内容不输出。
答案:
###(6)re.finditer(pattern, string, flags=0)
re.finditer()
搜索字符串,返回一个非重复匹配结果的迭代类型,每个迭代元素是match对象。
例如寻找关键词在文本中出现的所有起始位置:
(7)re.sub(pattern, repl, string, count=0, flags=0)
re.sub()
在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串
参数说明
pattern是匹配的正则表达式
repl : 替换的字符串,也可为一个函数。 例如
def capitalize(match): return match.group(1).upper+match.group(2).lower()
string是要被查找替换的字符串
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。count必须是非负整数。
例如
这里的pattern是匹配一个def函数,将其替换成static PyObject*\npy_\1(void)\n{
,这里面的\1
是pattern匹配字符串的第一个子表达式,即([a-zA-Z_][a-zA-Z_0-9]*)
匹配到的内容,在这里就是myfunc
。
再来看一个替换字符串repl为函数的例子:
-{1,2}
匹配到两个--
和一个-
,通过函数dashrepl将连续的--
替换成-
,将-
替换成空格,因此得到pro--gram files
。
第二个例子中,\sAND\s
匹配到And及其前后的2个空格,将其替换成' & '。
另外,空匹配只在不相临连续的情况被更替,所以 re.sub('x*', '-', 'abxd')
返回 '-a-b--d-'
。
作业1:在中英文字符之间加上空格
例如文本"今天真开心happy快乐",加上空格后变成"今天真开心 happy 快乐"
答案:
两个表达式是类似地,只不过第一个表达式是在英文和中文字符之间加上空格,第二个表达式是在中文和英文字符之间加上空格。我们来看第一个表达式。第一个()是匹配1个或多个英文,第二个()是匹配1个或多个中文字符,r'\1 \2'是匹配第1个分组和第2个分组,且中间加了空格。
作业2:将格式如"年-月-日"的日期表示法替换成"日/月/年"的方法表示。
答案:
因为\1
,\2
不是字符串中的合法转义序列,所以必须指定为原生字符串,在字符串前面加一个"r"。
如果想引用整个表达式匹配的文本,不能使用\0
,因为\0
开头的转义序列通常表示用八进制形式表示的字符,\0
本身表示ASCII字符编码为0的字符。如果一定要引用整个表达式匹配的文本,则可以稍加变通,给整个表达式加上一对括号,之后用\1
来引用。
\num
的用法可能产生二义性,例如\10
是表示第10个捕获分组还是第1个捕获分组之后跟着一个字符0呢?
在Python中,\10
会被解释成第10个捕获分组匹配的文本,下面的程序会报错:
如果希望效果是第1个捕获分组之后跟着一个字符0,那就要写成\g<1>0
(8)re.subn(pattern, repl, string, count=0, flags=0)
行为与 sub()
相同,但是返回一个元组 (字符串, 替换次数)
.
(9)re.escape(pattern)
转义 pattern 中的特殊字符。
(10)re.purge()
清除正则表达式的缓存。
二、Match对象
匹配对象支持以下方法和属性
(1)Match.expand(template)
对template进行反斜杠转义替换并且返回。转义如同 被转换成合适的字符,数字引用(\1
, \2
)和命名组合(\g<1>
, \g<name>
) 替换为相应组合的内容。
###(2)Match.group([group1, ...])
返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串;如果有多个参数,结果就是一个元组;如果没有参数,整个匹配都被返回。如果一个组N 参数值为 0,相应的返回值就是整个匹配字符串;如果它是一个范围 [1..99],结果就是相应的括号组字符串。如果一个组号是负数,或者大于样式中定义的组数,一个 IndexError
索引错误就 raise
。如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配。
另一个例子:
注意,如果被匹配多次,就返回最后一个匹配:
上面的分组编号是1、2、3等数字编号,当分组多了之后难以记忆,而且很难对应到希望捕获的相应分组上。这时可以用命名分组。
命名分组也就是标识分组为容易记忆和辨别的名字,而不是数字编号。
Python中用(?P<name>regex)
来分组。例如:
即便使用了命名分组,每个命名分组同时也具有数字编号。
Python中,如果使用了命名分组,在表达式反向引用时,必须使用(?P=name)
的记法,而要进行正则表达式替换,则要写作\g<name>
###(3)Match._getitem_(g)
等价于m.group(g)
###(4)Match.groups(default=None)
返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 default 参数用于不参与匹配的情况,默认为 None
。
如果我们使点号可选,那么不是所有的组都会有匹配结果。这些组合默认会返回一个 None
,除非指定了 default 参数。
###(5)Match.groupdict(default=None)
返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为 None
。 例如
###(6)Match.start(group=0)和Match.end(group=0)
返回 group 匹配到的字串的开始和结束位置。group 默认为0(意思是整个匹配的子串)。如果 group 存在,但未产生匹配,就返回 -1
。
###(7)Match.span(group=0)
对于一个匹配 m , 返回一个二元组 (m.start(group), m.end(group))
。 注意如果 group 没有在这个匹配中,就返回 (-1, -1)
。group 默认为0,就是整个匹配。
###(8)Match.pos和Match.endpos
pos和endpos的值,会传递给 search()
或 match()
的方法一个正则对象。这个是正则引擎开始(对应pos)或停止(对应endpos)在字符串搜索一个匹配的索引位置。
###(9)Match.lastindex
捕获组的最后一个匹配的整数索引值,如果没有匹配产生的话就是 None
。
例如:
###(10)Match.lastgroup
最后一个匹配的命名组名字,如果没有产生匹配的话就是 None
。
###(11)Match.re
返回产生这个实例的正则对象, 这个实例是由 正则对象的 match()
或 search()
方法产生的。
###(12)Match.string
传递到match()或search()的字符串。
三、flags参数
flags参数是正则表达式使用时的控制标记。控制标记用来改变表达式的行为模式,可以用模式修饰符来指定,也可以用预定义的常量(也就是flags的取值)作为特殊参数传入来指定。
模式修饰符即模式名称对应的单个字符,使用时将其填入特定结构(?modifier)
中(modifier为模式修饰符),嵌在正则表达式的开头。
模式修饰符写在最开头表示整个正则表达式都指定此模式,如果出现在中间,则表示此模式从这里开始生效;如果出现在某个括号内,那么它的作用范围只限于括号内部。Python的情况不同,不管出现在哪个位置,都对整个正则表达式生效。
flags参数的可取值及其对应的模式修饰符有以下几种:
1.re.A或re.ASCII: 对应模式修饰符a
。让 \w
, \W
, \b
, \B
, \d
, \D
, \s
和 \S
只匹配ASCII,而不是Unicode。Python3以上的版本中,正则表达式默认采用Unicode匹配规则(\w
能匹配Unicode中的“单词字符”,包括中文字符,\d
也能匹配1、2之类的全角数字字符。),如果希望让\d
, \w
等字符组简记法恢复到ASCII匹配规则,可以使用此模式。
2.re.DEBUG: 显示编译时的debug信息。
如果遇到复杂的表达式,或者不确定某个表达式的意义,可以通过它来观察。
缩进表示了表达式各结构的层级关系,而字符本身则显示为其码值的十进制表示(比如字符a的码值是十进制的97)。
3.re.I或re.IGNORECASE: 对应模式修饰符i
。忽略正则表达式的大小写,使得正则表达式如[A‐Z]
能够匹配小写字符。Unicode匹配(比如 Ü
匹配 ü
)同样有用,除非设置了re.ASCII
标记来禁用非ASCII匹配。
4.re.L或re.LOCALE: 对应模式修饰符 L
。由当前语言区域决定 \w
, \W
, \b
, \B
和大小写敏感匹配。这个标记只能对byte样式有效。这个标记不推荐使用,因为语言区域机制很不可靠,它一次只能处理一个"习惯",而且只对8位字节有效。Unicode匹配在Python 3 里默认启用,并可以处理不同语言。
5.re.M或re.MULTILINE: 对应模式修饰符m
。多行匹配,影响^
和$
。这种模式下正则表达式中的^
操作符能够将给定字符串的每行当作匹配开始。默认情况下^
是匹配整个字符串的开始。
一个多行模式的例子,这里是匹配每一行以数字开头的内容。
等价于
如果不使用多行模式的话,只能匹配到1 line
即第一行的内容。
6.re.S或re.DOTALL: 对应模式修饰符 s
。使得正则表达式中的.
操作符能够匹配所有字符,注意.
默认匹配除换行外的所有字符。这也称为单行模式。单行模式下所有文本似乎只在一行里,换行符变成了普通字符,因此点号可以匹配。
7.re.U或re.UNICODE:对应模式修饰符 u
。此模式下,\w
, \d
, \s
等字符组简记法的匹配规则会发生改变,比如\w
能匹配Unicode中的“单词字符”,包括中文字符,\d
也能匹配1、2之类的全角数字字符。在Python3中默认字符串已经是Unicode了,所以这个模式是冗余的。
8.re.X或re.VERBOSE: 对应模式修饰符 x
。这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。空白符号会被忽略,除非在一个字符集合当中或者由反斜杠转义,或者在 *?
, (?:
or (?P<…>
分组之内。当一个行内有 #
不在字符集和转义序列,那么它之后的所有字符都是注释。
意思就是下面两个正则表达式等价地匹配一个十进制数字:
如果记不住这么多取值也没关系,只需要知道常用的几个就可以了,即不区分大小写模式re.I、单行模式re.S、多行模式re.M、注释模式re.X。
如果需要同时使用多种模式,只要将模式修饰符排列起来就可以了。比如(?mx)
表示同时使用多行模式和注释模式。或者将预定义常量用|
组合起来,比如re.M|re.X
。
四、re.compile函数
compile函数将正则表达式编译成一个正则表达式对象,可以用于匹配。
函数语法:re.compile(pattern, flags=0)
pattern是正则表达式,flags的含义和re.search中的一样。
等价于
如果正则表达式需要多次使用的话,用re.compile()
保存正则表达式对象更加高效。
编译后的正则表达式对象支持以下方法和属性:
(1) Pattern.search(string, pos=0, endpos=len(string))
作用和re.search()类似。参数pos给出了字符串中开始搜索的位置索引,参数endpos限定了字符串搜索的结束。即只有从pos到endpos-1的字符会被匹配。如果endpos小于pos,就不会有匹配产生。
(2)Pattern.match(string, pos=0, endpos=len(string))
作用和re.match()类似。参数含义和Pattern.search()含义相同。
(3)Pattern.fullmatch(string, pos=0, endpos=len(string))
作用和re.fullmatch()类似。参数含义和Pattern.search()含义相同。
下面的几个函数都和对应的re函数类似,不再赘述。
(4)Pattern.split(string, maxsplit=0)
(5)Pattern.findall(string, pos=0, endpos=len(string))
(6)Pattern.finditer(string, pos=0, endpos=len(string))
(7)Pattern.sub(repl, string, count=0)
(8)Pattern.subn(repl, string, count=0)
(9)Pattern.flags
正则匹配标记,这是可以传递给compile()的参数。
(10)Pattern.groups
捕获到的模式串中组的数量
(11)Pattern.groupindex
映射由 (?P<id>)
定义的命名符号组合和数字组合的字典。如果没有符号组,那字典就是空的。
(12)Pattern.pattern
编译对象的原始样式字符串。
五、其它
条件匹配
Python的正则表达式支持条件匹配。语法是(?(id/name)yes-pattern|no-pattern)
。其中id/name是对应捕获分组的名称或者编号,如果该捕获分组成功匹配文本,则后续匹配交由yes-pattern来完成,否则交由no-pattern来完成。no-pattern可以省略。
假设我们需要验证价格:如果前面没有美元符号$,则价格只能包含整数部分,否则还应当包含小数点和两位小数。
其中(?(1)\.[0-9]{2}|)
就是一个条件匹配表达式,1对应的是(\$)
捕获到的内容。如果起始是$
符号,则后面的价格部分除了整数部分外,还必须包含小数点和2位小数,即\.[0-9]{2}
;如果起始不是$
符号,价格只能包含整数部分,不能有其它的。这里的no-pattern是省略的。
参考资料
https://docs.python.org/zh-cn/3.9/library/re.html
Last updated
Was this helpful?