Python 笔记五之正则表达式
本文首发于公众号:Hunter 后端
原文链接:Python笔记五之正则表达式
这一篇笔记介绍在 Python 里使用正则表达式。
正则表达式,Regular Expression,可用于在一个目标字符串里对于指定模式的字符进行查找、替换、分割等操作。
比如,判断某个字符串里是否都是数字,或者是否包含指定字符串,又或者更直接的例子是判断电话号码或者邮箱是否合法等。
这一篇笔记里,我们将先介绍一个正则表达式的函数,并以此来引入正则表达式的各种模式,并辅以各个例子进行介绍。
u1s1,正则表达式,我学了三四遍,学一遍忘一遍,忘一遍学一遍,只要隔一阵子不用就会忘,所以这一篇笔记力求将正则表达式的所有模式和用法都记录下来,用作之后的查找笔记。
以下笔记使用 Python 版本是 3.8。
以下是本篇笔记目录:
re.findall
表达式语法
匹配字符串类型
\b-匹配空字符串,但只在单词开始或结尾
\B-匹配空字符串,不能在开头或结尾
\d-匹配十进制数字
\D-匹配非十进制字符
\s-匹配空白字符
\S-匹配非空白字符
\w-匹配字符
\W-匹配非单词字符
匹配字符串出现位置
\A-只匹配字符串开始
\Z-只匹配字符串结尾
^-匹配字符串开头
$-匹配字符串结尾
匹配字符串数量
*-匹配重复 0 到 n 次
+-匹配重复 1 到 n 次
?-匹配重复 0 到 1 次
{}-指定匹配重复次数 a. {m}-只匹配重复 m 次 b. {m,n}-匹配重复 m 到 n 次
匹配字符串集合
字符可以单独列出
可以表示字符范围
特殊字符在集合里只会匹配其原始字符含义
字符类 \s 或 \w 可以在集合里使用
取反操作可以使用 ^
其他匹配类型
|-表达式的或操作
()-匹配括号内的任意正则表达式
常用正则方法
re.search
re.match
re.split
re.findall
re.finditer
re.sub
re.subn
re.compile
其他参数
re.I
re.M
re.Match 匹配对象
Match.group()
Match.__getitem__(g)
Match.groups()
Match.re
Match.string
Match.start() 和 Match.end()
Match.span()
1、re.findall
使用正则表达式,首先要引入模块:
这里从 re.findall
开始介绍,findall 方法表示的是找到目标字符串里符合指定模式的全部数据。
比如我们有一个字符串 abcdefg
,想要从其中找到 de
,就可以如下操作:
返回的就是一个列表,列表元素是我们的目标字符串 de
:
我们的 target_str 就是一个匹配模式,这里是一个纯字符串,我们可以将其替换成其他的模式字符串。
接下来我们将分类介绍正则表达式语法。
2、表达式语法
正则表达式有很多种,比如表示匹配数量的 +
,*
,?
,还有表示匹配字符串内容的 \s
,\w
等。
下面将从这些类别分别开始介绍:匹配字符串类型、匹配字符串出现位置、匹配字符串数量、匹配字符串集合等
1. 匹配字符串类型
1) \b-匹配空字符串,但只在单词开始或结尾
\b 可以匹配空字符串,但是只在字符串开始或者结尾的位置,这里的空字符串涵盖了标签符号,比如 , . ,。?
等,也包括换行制表符 \n \t
等,也包含 '',可以理解为字符串的边界部分。
所以 \b
的作用其实就可用于匹配特定字符串的前缀或者后缀,这里的前缀和后缀并不仅仅是指整个字符串的前缀和后缀,也包括字符串内部被分割的前缀和后缀。
比如对于下面这个字符串:
i have a apple
我们想找到是否有 ha
开头的单词,可以如此操作:
字符串如果是以下几种情况,也可以匹配上:
我们还可以使用 \b 来匹配特定的单词,在英文中,单词的出现是前后都有空格或者标点符号的,那么我们就可以前后都加上 \b 来限定匹配是否出现过此单词:
2) \B-匹配空字符串,不能在开头或结尾
\B 是 \b 的取非操作,含义是匹配空字符串,但是不能出现在开头或者结尾,也就是说 \B 所在的位置必须有 1 至多个非空字符串来替代:
3) \d-匹配十进制数字
\d 用来匹配十进制数字,也就是 0-9 这些,比如下面的操作:
可以看到返回的结果是分隔开的数字,如果想要他们在一起返回,我们可以使用 \d+
来操作,+
表示的匹配 1 到 n 次,这个后面再介绍。
4) \D-匹配非十进制字符
\D 表示的是 \d 相反的操作,非十进制字符,可以使用上面的示例进行测试。
5) \s-匹配空白字符
\s 匹配的空白字符不包括标点符号,常见的有换行符,制表符,回车符等转义字符,\n \t \r \f
等
6) \S-匹配非空白字符
\S 是 \s 取非操作,除了上面的换行符、制表符等字符外,包括标点符号皆可被匹配上
7) \w-匹配字符
\w 不匹配换行符、制表符等转义字符,不匹配中英文常见标点符号,比如 , . ; "
等,但是可以匹配中英文字符、数字和下划线,比如下面的示例:
8) \W-匹配非单词字符
\W 是 \w 的取反操作。
2. 匹配字符串出现位置
前面介绍的匹配字符串的类型,这里介绍匹配出现的位置,比如开头或者结尾。
1) \A-只匹配字符串开始
\A 只匹配字符串的开始,也就是我们所说的字符串前缀:
与字符串的 startswith() 函数在匹配功能上是一样的。
2) \Z-只匹配字符串结尾
\Z 只匹配字符串结尾部分,也就是所说的字符串后缀:
3) ^-匹配字符串开头
^ 也是只匹配字符串的开头,但是与 \A 不同的是,使用 re.M 模式下,也可以匹配换行符后的开头,比如下面的示例,就可以返回两个结果:
如果去掉 re.M,则会退化成 \A 的功能
4) $-匹配字符串结尾
$ 也是只匹配字符串的结尾,但是在 re.M 模式下,也可以匹配换行符后新一行的结尾,比如下面的示例,可以返回两个结果:
同理,如果去掉 re.M,则会退化成 \Z 的功能。
3. 匹配字符串数量
除了匹配指定模式的内容,字符位置,我们还可以匹配指定模式的数量,比如想只要满足一个即可返回,或者尽可能多的将满足的字符返回等。
接下来一一介绍如何匹配字符串数量。
1) *-匹配重复 0 到 n 次
*
表示匹配的数量可以重复从 0 到 n 次,这个 n 为任意次,尽量多的匹配字符串。
比如我们想匹配 a
以及 a
后面可以加上任意个 b
字符,比如希望 a
,ab
,abb
,abbbb
等都可以被匹配上。
那么就可以使用下面的操作:
2) +-匹配重复 1 到 n 次
+
表示匹配的数量可以重复从 1 到 n 次,提前条件是匹配模式必须出现一个。
还是上面的例子,我们希望可以匹配上 ab
,abbb
,以及无限多个 b
,但是不可匹配 a
,可以如下操作:
3) ?-匹配重复 0 到 1 次
?
表示匹配的数量只能出现 0 到 1 次。
比如对于一个字符串,我们想匹配 apple
这个单词,但是我们也想使 apple
的复数形式 apples
也能被匹配上,所以我们这里的 s
希望它出现的次数是 0 到 1 次
4) {}-指定匹配重复次数
使用花括号可以指定重复的次数,可以是一个固定的值,也可以是一个范围,下面分别介绍一下。
a. {m}-只匹配重复 m 次
{m}
表示匹配重复次数为 m,比如我们想要匹配 abbb
,也就是 a
字符后重复出现三个 b
,可以如下操作:
b. {m,n}-匹配重复 m 到 n 次
{m,n}
表示匹配重复次数为 m 到 n 次,比如我们想要 a
后面跟着 3,4,5 个 b
都可以接受,可以如下操作:
4. 匹配字符串集合
我们可以使用中括号 []
来限定字符串或者匹配模式的集合,也就是说我们可以将我们想要匹配的字符串或者类型都加到 []
里,满足条件的都可以被匹配返回。
1) 字符可以单独列出
如果我们想匹配某些单个字符,可以单独列出来操作,比如 a, t, w, q,可以使用 [atwq],以下是示例:
2) 可以表示字符范围
我们可以使用 -
来表示字符的范围进行匹配,比如 a-z
,或者数字类型的 0-9
,比如下面的操作:
注意: 在这里的连接符 -
是表示范围的,如果只是想匹配 -
字符,需要使用 \
进行转义,或者将 -
放在首位或者末位:
上面的这个操作表示的是希望匹配上 -
a
和 z
三个字符。
3) 特殊字符在集合里只会匹配其原始字符含义
特殊字符,比如前面表示数量的 *
+
等字符在中括号里就匹配的是对应的星号和加号:
4) 字符类 \s 或 \w 可以在集合里使用
比如下面的操作,可以 \W 和 0-9 之间的字符:
5) 取反操作可以使用 ^
如果要取反,意思是集合里的匹配模式都不匹配,比如我们想匹配字符串里的非数字,可以如下操作:
5. 其他匹配类型
1) |-表达式的或操作
比如我们有两个表达式 A 和 B,只要有一个符合条件即可,即匹配模式的或操作:
2) ()-匹配括号内的任意正则表达式
小括号表示分组,可以将匹配模式分组放到多个小括号内进行匹配,匹配后的结果也会以元组的形式分组返回。
比如我们想匹配 英文字母+数字
的形式,并且以括号的形式将其分隔开,那么返回的匹配结果也会以元组的形式将其返回:
如果是匹配上了多个结果,那么多个结果会以列表的形式返回,元素也是匹配的结果以元组的形式返回:
3、常用正则方法
前面介绍了 re.findall() 方法,返回的是一个列表,元素是所有匹配上的结果,接下来介绍一些其他常用的正则方法。
1. re.search
re.search() 方法的作用是扫描整个目标字符串,找到匹配上的第一个位置,然后返回一个匹配对象,如果没有满足要求的数据,则返回 None。
这里要注意三点,一点是扫描整个字符串,直到找到匹配的对象,另一点是找到一个符合条件的字符串以后就停止扫描,即便后面还有符合条件的,三是返回一个匹配对象,关于匹配对象下面再介绍。
比如下面的示例是匹配 英文字母+数字:
可以看到这里,其实有多个符合匹配模式的数据,如果这里使用 re.findall() 会返回多个值,但这里返回的只是字符串里第一个符合条件的数据。
至于如何获取返回的这个 re.Match 对象详情数据,见第四节 re.Match 匹配对象
,建议先阅读该章节再往后读。
2. re.match
re.match() 方法也是用于匹配指定模式,效果上与 re.search() 无异,唯一不同的一点是 match() 方法只能从字符串的头部开始匹配,返回的也是一个 re.Match 对象。
比如下面的操作,第一种形式匹配不到数据,返回 None,第二种就可以返回 re.Match 对象:
3. re.split
根据匹配模式将指定字符串进行分割,在效果上相当于 string.split() 的增强版。
因为 string.split() 不能使用正则对象来对字符串进行切割,而 re.split() 可以实现。
比如我们想要根据 1,2,3 对指定字符串进行切割,就可以用到 |
这个操作:
我们也可以使用其他正则对象,比如我们想要根据字符串中的标点符号,空格等对字符串进行切割:
re.split() 还可以接受 maxsplit 参数,表示最多切割的次数:
4. re.findall
re.findall() 前面有过介绍,根据匹配模式获取所有的匹配字符,结果以列表形式返回,元素为匹配的字符串:
5. re.finditer
re.finditer() 函数与 re.findall() 函数效果类似,都是找到目标字符串里全部满足条件的字符串,但是不同的是 finditer() 返回的一个迭代器,迭代器保存的是匹配对象,也就是 re.Match 对象:
6. re.sub
替换函数,将字符串里的指定模式替换成目标字符串,然后返回:
7. re.subn
re.subn() 与 re.sub() 函数作用一致,都是替换目标字符串,但是返回的是一个元组,分别是替换后的字符串和替换的次数:
8. re.compile
re.compile() 函数将正则表达式编译为一个正则表达式对象,然后可以调用前面介绍过的这些正则函数,比如 re.search(),re.match(),re.findall() 等。
re.complie() 这个函数的操作可以用于永福匹配模式,使程序更加高效。
9. 其他参数
接下来介绍一下正则模块在匹配情况下的一些其他参数。
1) re.I
忽略大小写。
对于目标字符串,如果存在字母大小写的情况,我们可以对原始字符串统一进行大写或者小写的操作,然后进行匹配,以忽略大小写的影响,也可以使用 re.I 参数:
2) re.M
多行匹配模式。
前面介绍 ^
和 $
这两个符号的匹配的时候介绍过,如果加上 re.M 参数,表示的是可以匹配字符串内部有换行符的开头和结尾的数据:
4、re.Match 匹配对象
对于一些函数,比如前面介绍的 re.search(),返回的就是一个 re.Match 对象,还是以上面的示例为例,如何获取 re.Match 中的数据呢,下面开始介绍。
1. Match.group()
group() 函数直接调用,或者添加参数 0,返回的是匹配的字符串数据:
而如果我们匹配的模式使用了小括号 ()
进行了分组,那么则可以对 group() 函数添加其他 index 表示匹配的分组的结果,比如下面的操作:
2. Match.__getitem__(g)
re.Match 对象实现了 getitem 这个魔术方法,所以我们可以通过索引的方式对来访问匹配的结果,其效果和 group(index) 是一致的:
3. Match.groups()
groups() 函数返回的是进行了分组匹配的结果,以元组的形式返回。
比如这里分组的 result1 对象:
而 result 这个 Match 对象匹配模式里并没有使用小括号进行分组,所以返回的结果是空元组:
4. Match.re
返回的是生成这个 Match 对象的正则对象,也就是我们输入的 target_pattern:
5. Match.string
返回的是传递到生成这个 Match 对象的原始字符串:
6. Match.start() 和 Match.end()
这两个函数表示的是匹配上的字符串的开始位置和结束位置:
我们对原始字符串进行起始位置的截取就是 Match.group() 的结果:
而如果我们前面对匹配模式进行了分组的操作,也就是使用了小括号,比如返回的 result1,我们可以想 start() 和 end() 函数添加索引参数分别表示这两个分组结果开始和结束的下标位置:
7. Match.span()
返回的是匹配结果的开始和结束位置,以元组的形式返回,其实就是一次性返回 Match.start() 和 Match.end() 的结果。
span() 函数也可以接受分组的参数返回分组的起始位置。
版权声明: 本文为 InfoQ 作者【Hunter熊】的原创文章。
原文链接:【http://xie.infoq.cn/article/482acf08d1a807bb2821800b6】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论