python正则表达式
这篇博客主要记录自己在python学习中正则表达式的内容,主要是以下四个内容:
- 简介/动机
- 正则表达式字符
- python中使用正则表达式
- 几点特别说明
简介/动机
正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。—— [ 维基百科 ]
一句话来讲,正则表达式就是要实现对文本(一般来讲,就是字符串)的处理,包括添加、删除、分离、叠加、插入等各种操作。比如解决下面场景中的一些问题
- 检索某台web服务器上的重复单词
- 筛选出你的所有邮件中具有特定主题的邮件,进行其他操作(删除、转发)。
正则表达式字符
正则表达式的字符是由普通字符(纯文本)或元字符(具有特殊含义的特殊字符)组成的;正则表达式的一个术语名称是模式(pattern)
符号 | 说明 |
---|---|
. | 匹配任意单个字符,(换行符\n除外) |
? | 匹配0次或1次前面出现的正则表达式 |
* | 匹配0次或多次前面出现的正则表达式 |
+ | 匹配1次或多次前面出现的正则表达式 |
^ | 匹配字符串的起始部分 |
$ | 匹配字符串终止部分 |
re1 | re2 | 匹配正则表达式re1 或re2 |
{ N } | 匹配n次前面出现的正则表达式 |
{M, N} | 匹配M~N次前面出现的正则表达式 |
[ ] | 匹配来自字符集的任意单一字符 |
[x-y] | 匹配x~y范围中的任意单一字符 |
[^ ] | 不匹配字符集出现的任何一个字符,包括某一范围的字符 |
( ) | 匹配封闭的正则表达式,然后另存为子组 |
下表是由 \和字母组成的特殊字符
表示法 | 描述 | 同义替换 |
---|---|---|
\d | 匹配任何十进制数字 | [0-9] |
\D | 匹配非数字 | |
\w | 匹配任何字母数字字符 | [0-9A-Za-z] |
\W | 与\w相反 | |
\s | 匹配任何空格字符 \S与之相反 | [\n\t\r\v\f] |
\b | 匹配任何单词边界\B与之相反 | |
\A | 匹配字符串开始 | ^ |
\Z | 匹配字符串的结尾 | $ |
还有一些是扩展表示法
表示法 | 描述 | 示例 |
---|---|---|
(?P < name>) | 由name标识的正则分组匹配 |
注:扩展表示法相对而言比较难以理解,解释晦涩。
接下来对上表中的一些正则表达式用法做一个具体的解释。利用上表的正则表达式语法实现具体的功能。
从字符串起始或者结尾或者单词的边界匹配
用于实现该功能的正则表达式有 ^ 、 \A 、 $ 、\Z、 \b、 \B ,其中 ^ 和 \A实现从字符串的开始位置进行匹配; $ 和 \Z 用于匹配字符串的末尾位置。特殊字符\b实现匹配字符边界,\B与之相反;下面是一些示例
正则表达式模式 | 匹配的字符串 |
---|---|
^From | 任何以From作为起始字符串的字符串 |
end$ | 任何以end作为结尾的字符串 |
\bthe | 任何以the开始的字符串 |
\bthe\b | 仅仅匹配单词the |
\Bthe | 包含但不以the作为起始的字符串 |
字符集的功能
利用字符集可以实现以下功能:
- 匹配方括号中任意一个字符
- 匹配指定的字符范围(方括号中两个符号中间用-连接)
- 不匹配给定字符集中的任何一个字符(^紧跟在左方括号后面)
正则表达式模式 | 匹配的字符串 |
---|---|
b[ai]t | bat 、bit |
z.[0-9] | z后面跟着任何一个字符,然后跟一个数字 |
[^\t\n] | 不匹配制表符或者\n |
使用圆括号指定分组
一对圆括号可以实现以下两个功能:
- 对正则表达式进行分组(重复操作符,而不是单一字符或者字符集)
- 匹配子组(实现分别访问每一个子组)
python中使用正则表达式
python中为了利用正则表达式实现对字符串的处理,提供了一些函数,这些函数被封装在re模块中,下面是re模块的常用的核心函数和方法
函数名称 | 功能 | 备注 |
---|---|---|
compile(pattern,flags =0) | 使用可选的标记编译正则表达式模式,返回一个正则表达式对象 | 仅仅是re模块函数 |
search(pattern,string,flags = 0) | 搜素字符串中第一次出现的正则表达式模式,返回匹配对象 | re模块函数和正则表达式对象的方法 |
match(pattern,string,flags = 0) | 从头开始匹配,,返回匹配对象 | 同上 |
findall (pattern,string [,flags]) | 把所有匹配到的字符放到以列表中的元素返回 | 同上 |
finditer(pattern,string [,flags]) | 与findall函数相同,但返回的是一个迭代器 | 同上 |
split(pattern,string ,max = 0) | 根据正则表达式的模式进行分割,返回一个列表 ,分割最多操作max次,默认分割所有匹配成功的函数 | 同上 |
sub (pattern,repl,string,count = 0) | 使用repl替换所有正则表达式的模式在字符串中出现的位置,除非定义count,否则就替换所有出现的位置 | 同上 |
group (num =0) | 返回整个匹配对象,或者编号为num的特定子组 | 匹配对象(如match、search返回的对象)的方法 |
groups | 返回一个包含所有匹配对象的元组 | 同上 |
groupdict | 返回一个包含有所有匹配的命名子组的字典,所有的子组名称作为字典的键值 | 同上 |
re.I | 不区分大小写的匹配 | 模块属性 |
re.M | 多行匹配 | 模块属性 |
re.S | 匹配全部的字符 包含换行符 | 模块属性 |
注:红色标注的为重点需掌握的内容。
下面通过代码来演示如何使用re模块
compile 演示
在模式发生匹配之前,正则表达式模式必须编译成正则表达式对象。因此如果在比较之前提前对正则表达式模式进行编译(预编译过程),可以提高程序的运行效率,re模块的compile函数提供了对正则表达式进行编译的功能,编译后,返回一个正则表达式对象,这也是为什么一开始就介绍compile函数的意图。
import re
# 使用compile进行预编译和不使用该函数时的一个例子
obj = re.compile('\.com') # 对正则表达式进行编译,返回的正则表达式对象可以被多次调用
ret = obj.search('xxx.com')
print(ret) # <_sre.SRE_Match object; span=(3, 7), match='.com'>
print(ret.group()) # group 返回匹配对象
ret_1 = re.search('\.com', 'xxx.com')
print(ret_1)
print(ret_1.group())
运行结果展示:
match和search比较
match和search最大的区别在于search在任意位置进行搜索匹配,而match必须从开头进行匹配。
import re
# 使用match、search的例子
str = 'food on the table'
str_1 = 'the man is fool'
m = re.match('foo', str)
m_1 = re.match('foo', str_1) # match 无法匹配到 ,match只能从起始位置匹配
m_2 = re.search('foo', str_1) # search 可以匹配到
if m is not None : print('print m', m.group())
if m_1 is not None : print('print m_1', m_1.group())
if m_2 is not None : print('print m_2', m_2.group())
运行结果展示:
findall 、finditer和sub 使用
findall 和 finditer的区别在于返回对象不同,findall 返回一个列表;finditer返回一个迭代器对象。
import re
# findall finditer sub 示例
str = 'this and thatis'
ret_findall = re.findall('th\w+', str) # 在这个示例中,将匹配字符串中this 和 that 并且以列表的形式返回
ret_finditer = re.finditer('th\w+',str) # 功能同上,返回迭代器
ret_sub = re.sub('th\w+','good',str) # 用good 对 this和 that进行替换
print(ret_findall)
print(next(ret_finditer).group()) # 利用next对迭代器对象区取值
print(next(ret_finditer).group())
print(ret_sub)
运行结果如下:
分组及group、 groups比较使用
使用()可以用来保存分组,以便于后续处理,使用group方法访问每个独立的子组以及groups()方法获取一个包含所有匹配子组的元组
import re
# 分组示例
m_group = re.match('(\w\w\w)-(\d\d\d)', 'abc-123')
print(m_group.group()) # 完整匹配
print(m_group.group(1)) # 子组1
print(m_group.group(2)) # 子组2
print(m_group.groups()) # 全部子组
运行结果如下:
split的一个复杂例子
re模块的split()方法和string自带的split方法是类似的,但更为强大和灵活。下面是一个较为复杂的例子,一个用于web站点的简单解释器,用户输入城市名和州名 ,或者城市名加上ZIP码(5个数字),三者不一定全部都有。
import re
# split复杂例子
DATA = ('Mountain View,CA 94040',
'Sunnyvale CA',
'Los Altos,94023',
'Cupertino 95014',
'Palo Alto CA')
for datun in DATA:
print(re.split(',|(?= (?:\d{5}|[A-Z]{2})) ',datun))
运行结果如下:
注:对这个例子中出现的扩展字符的用法理解的还不是很透彻
正则表达式在python中的示例
几点特别的说明
搜索匹配与贪婪
正则表达式本质上实现贪婪匹配,这就意味着对于通配符模式,将对正则表达式从左向右的顺序求值,而且试图获取匹配该模式尽可能多的字符。
使用非贪婪操作符?,在* + ?使用该操作符。该操作符要求正则表达式匹配尽可能少的字符。
使用python原始字符(转义字符\的问题)
我们知道,可以用\n表示换行,用\d表示表示匹配的单个数字。如果有符号同时用于ASCII和正则表达式,就会产生问题,因此可以利用python原始字符串来避免这种问题,即在正则表达式模式前加r。