本文首发于公众号:Hunter 后端
原文链接:Python 笔记五之正则表达式
这一篇笔记介绍在 Python 里应用正则表达式。
正则表达式,Regular Expression,可用于在一个指标字符串里对于指定模式的字符进行查找、替换、宰割等操作。
比方,判断某个字符串里是否都是数字,或者是否蕴含指定字符串,又或者更间接的例子是判断电话号码或者邮箱是否非法等。
这一篇笔记里,咱们将先介绍一个正则表达式的函数,并以此来引入正则表达式的各种模式,并辅以各个例子进行介绍。
u1s1,正则表达式,我学了三四遍,学一遍忘一遍,忘一遍学一遍,只有隔一阵子不必就会忘,所以这一篇笔记力求将正则表达式的所有模式和用法都记录下来,用作之后的查找笔记。
以下笔记应用 Python 版本是 3.8。
以下是本篇笔记目录:
- re.findall
-
表达式语法
-
匹配字符串类型
- \b- 匹配空字符串,但只在单词开始或结尾
- \B- 匹配空字符串,不能在结尾或结尾
- \d- 匹配十进制数字
- \D- 匹配非十进制字符
- \s- 匹配空白字符
- \S- 匹配非空白字符
- \w- 匹配字符
- \W- 匹配非单词字符
- 匹配字符串呈现地位
1) \A- 只匹配字符串开始
2) \Z- 只匹配字符串结尾
3) ^- 匹配字符串结尾
4) $- 匹配字符串结尾 -
匹配字符串数量
1) *- 匹配反复 0 到 n 次
2) +- 匹配反复 1 到 n 次
3) ?- 匹配反复 0 到 1 次
4) {}- 指定匹配反复次数a. {m}- 只匹配反复 m 次 b. {m,n}- 匹配反复 m 到 n 次
- 匹配字符串汇合
1) 字符能够独自列出
2) 能够示意字符范畴
3) 特殊字符在汇合里只会匹配其原始字符含意
4) 字符类 \s 或 \w 能够在汇合里应用
5) 取反操作能够应用 ^ -
其余匹配类型
- |- 表达式的或操作
- ()- 匹配括号内的任意正则表达式
-
-
罕用正则办法
- re.search
- re.match
- re.split
- re.findall
- re.finditer
- re.sub
- re.subn
- re.compile
- 其余参数
1) re.I
2) re.M
-
re.Match 匹配对象
- Match.group()
Match.__getitem__(g)
- Match.groups()
- Match.re
- Match.string
- Match.start() 和 Match.end()
- Match.span()
1、re.findall
应用正则表达式,首先要引入模块:
import re
这里从 re.findall
开始介绍,findall 办法示意的是找到指标字符串里合乎指定模式的全副数据。
比方咱们有一个字符串 abcdefg
,想要从其中找到 de
,就能够如下操作:
str_1 = "abcdefg"
target_str = "de"
print(re.findall(target_str, str_1))
返回的就是一个列表,列表元素是咱们的指标字符串 de
:
['de']
咱们的 target_str 就是一个匹配模式,这里是一个纯字符串,咱们能够将其替换成其余的模式字符串。
接下来咱们将分类介绍正则表达式语法。
2、表达式语法
正则表达式有很多种,比方示意匹配数量的 +
,*
,?
,还有示意匹配字符串内容的 \s
,\w
等。
上面将从这些类别别离开始介绍:匹配字符串类型、匹配字符串呈现地位、匹配字符串数量、匹配字符串汇合等
1. 匹配字符串类型
1) \b- 匹配空字符串,但只在单词开始或结尾
\b 能够匹配空字符串,然而只在字符串开始或者结尾的地位,这里的空字符串涵盖了标签符号,比方 , .,。?
等,也包含换行制表符 \n \t
等,也蕴含 ”,能够了解为字符串的边界局部。
所以 \b
的作用其实就可用于匹配特定字符串的前缀或者后缀,这里的前缀和后缀并不仅仅是指整个字符串的前缀和后缀,也包含字符串外部被宰割的前缀和后缀。
比方对于上面这个字符串:
i have a apple
咱们想找到是否有 ha
结尾的单词,能够如此操作:
str_1 = "i have a apple"
target_pattern = r"\bha"
print(re.findall(target_pattern, str_1))
字符串如果是以下几种状况,也能够匹配上:
str_1 = "i ,have a apple"
str_1 = "i \thave a apple"
str_1 = "i ha"
咱们还能够应用 \b 来匹配特定的单词,在英文中,单词的呈现是前后都有空格或者标点符号的,那么咱们就能够前后都加上 \b 来限定匹配是否呈现过此单词:
str_1 = "i have an apple"
str_1 = "i have an apple,"
str_1 = "i have an apple how are you"
target_pattern = r"\bapple\b"
print(re.findall(target_pattern, str_1))
2) \B- 匹配空字符串,不能在结尾或结尾
\B 是 \b 的取非操作,含意是匹配空字符串,然而不能呈现在结尾或者结尾,也就是说 \B 所在的地位必须有 1 至多个非空字符串来代替:
str_1 = "i have an apple"
target_pattern = r"app\B"
print(re.findall(target_pattern, str_1))
3) \d- 匹配十进制数字
\d 用来匹配十进制数字,也就是 0-9 这些,比方上面的操作:
str_1 = "asdas98123asa978d"
target_pattern = r"\d"
print(re.findall(target_pattern, str_1))
# ['9', '8', '1', '2', '3', '9', '7', '8']
能够看到返回的后果是分隔开的数字,如果想要他们在一起返回,咱们能够应用 \d+
来操作,+
示意的匹配 1 到 n 次,这个前面再介绍。
4) \D- 匹配非十进制字符
\D 示意的是 \d 相同的操作,非十进制字符,能够应用下面的示例进行测试。
5) \s- 匹配空白字符
\s 匹配的空白字符不包含标点符号,常见的有换行符,制表符,回车符等转义字符,\n \t \r \f
等
str_1 = "asdas9812\v3a\rs,.\ta9\n78\fd"
target_pattern = r"\s"
print(re.findall(target_pattern, str_1))
6) \S- 匹配非空白字符
\S 是 \s 取非操作,除了下面的换行符、制表符等字符外,包含标点符号皆可被匹配上
7) \w- 匹配字符
\w 不匹配换行符、制表符等转义字符,不匹配中英文常见标点符号,比方 , . ; "
等,然而能够匹配中英文字符、数字和下划线,比方上面的示例:
str_1 = "asd—— _a 你‘' 好,s9。8?12\v3a\rs,.\ta9\n78\fd"target_pattern = r"\w"
print(re.findall(target_pattern, str_1))
# ['a', 's', 'd', '_', 'a', '你', '好', 's', '9', '8', '1', '2', '3', 'a', 's', 'a', '9', '7', '8', 'd']
8) \W- 匹配非单词字符
\W 是 \w 的取反操作。
2. 匹配字符串呈现地位
后面介绍的匹配字符串的类型,这里介绍匹配呈现的地位,比方结尾或者结尾。
1) \A- 只匹配字符串开始
\A 只匹配字符串的开始,也就是咱们所说的字符串前缀:
str_1 = "asd—— _a 你‘' 好,\ns9。8?12\v3a\rs,.\ta9\n78\fd"target_pattern = r"\Aasd"
print(re.findall(target_pattern, str_1))
与字符串的 startswith() 函数在匹配性能上是一样的。
2) \Z- 只匹配字符串结尾
\Z 只匹配字符串结尾局部,也就是所说的字符串后缀:
str_1 = "asd—— _a 你‘' 好,\ns9。8?12\v3a\rs,.\ta9\n78d"target_pattern = r"d\Z"
print(re.findall(target_pattern, str_1))
3) ^- 匹配字符串结尾
^ 也是只匹配字符串的结尾,然而与 \A 不同的是,应用 re.M 模式下,也能够匹配换行符后的结尾,比方上面的示例,就能够返回两个后果:
str_1 = "asd—— _\na 你‘' 好,\ns9。8?12\v3a\rs,.\ta9\n78d"target_pattern = r"^a"
print(re.findall(target_pattern, str_1, re.M))
# ['a', 'a']
如果去掉 re.M,则会进化成 \A 的性能
4) $- 匹配字符串结尾
$ 也是只匹配字符串的结尾,然而在 re.M 模式下,也能够匹配换行符后新一行的结尾,比方上面的示例,能够返回两个后果:
str_1 = "asd—— _\na 你‘' 好,\ns9。8?12\v3a\rs,.\ta9d\n78d"target_pattern = r"d$"
print(re.findall(target_pattern, str_1, re.M))
同理,如果去掉 re.M,则会进化成 \Z 的性能。
3. 匹配字符串数量
除了匹配指定模式的内容,字符地位,咱们还能够匹配指定模式的数量,比方想只有满足一个即可返回,或者尽可能多的将满足的字符返回等。
接下来一一介绍如何匹配字符串数量。
1) *- 匹配反复 0 到 n 次
*
示意匹配的数量能够反复从 0 到 n 次,这个 n 为任意次,尽量多的匹配字符串。
比方咱们想匹配 a
以及 a
前面能够加上任意个 b
字符,比方心愿 a
,ab
,abb
,abbbb
等都能够被匹配上。
那么就能够应用上面的操作:
str_1 = "axxxxabbxxxxabbbb"
target_pattern = r"ab*"
print(re.findall(target_pattern, str_1))
# ['a', 'abb', 'abbbb']
2) +- 匹配反复 1 到 n 次
+
示意匹配的数量能够反复从 1 到 n 次,提前条件是匹配模式必须呈现一个。
还是下面的例子,咱们心愿能够匹配上 ab
,abbb
,以及有限多个 b
,然而不可匹配 a
,能够如下操作:
str_1 = "axxxxabbxxxxabbbb"
target_pattern = r"ab+"
print(re.findall(target_pattern, str_1))
# ['abb', 'abbbb']
3) ?- 匹配反复 0 到 1 次
?
示意匹配的数量只能呈现 0 到 1 次。
比方对于一个字符串,咱们想匹配 apple
这个单词,然而咱们也想使 apple
的复数模式 apples
也能被匹配上,所以咱们这里的 s
心愿它呈现的次数是 0 到 1 次
str_1 = "i have an apple, he has two apples, she has three apples"
target_pattern = r"apples?"
print(re.findall(target_pattern, str_1))
# ['apple', 'apples', 'apples']
4) {}- 指定匹配反复次数
应用花括号能够指定反复的次数,能够是一个固定的值,也能够是一个范畴,上面别离介绍一下。
a. {m}- 只匹配反复 m 次
{m}
示意匹配反复次数为 m,比方咱们想要匹配 abbb
,也就是 a
字符后反复呈现三个 b
,能够如下操作:
str_1 = "abbxxxxabbbbaxxxabbb"
target_pattern = r"ab{3}"
print(re.findall(target_pattern, str_1))
b. {m,n}- 匹配反复 m 到 n 次
{m,n}
示意匹配反复次数为 m 到 n 次,比方咱们想要 a
前面跟着 3,4,5 个 b
都能够承受,能够如下操作:
str_1 = "abbbxxxxabbbbbbaxxxabbbb"
target_pattern = r"ab{3,5}"
print(re.findall(target_pattern, str_1))
# ['abbb', 'abbbbb', 'abbbb']
4. 匹配字符串汇合
咱们能够应用中括号 []
来限定字符串或者匹配模式的汇合,也就是说咱们能够将咱们想要匹配的字符串或者类型都加到 []
里,满足条件的都能够被匹配返回。
1) 字符能够独自列出
如果咱们想匹配某些单个字符,能够独自列出来操作,比方 a, t, w, q,能够应用 [atwq],以下是示例:
str_1 = "asdqweasdaterq"
target_pattern = r"[atwq]"
print(re.findall(target_pattern, str_1))
# ['a', 'q', 'w', 'a', 'a', 't', 'q']
2) 能够示意字符范畴
咱们能够应用 -
来示意字符的范畴进行匹配,比方 a-z
,或者数字类型的 0-9
,比方上面的操作:
str_1 = "asdqweasdaterq"
target_pattern = r"[a-j]"
print(re.findall(target_pattern, str_1))
# ['a', 'd', 'e', 'a', 'd', 'a', 'e']
str_1 = "asd136q78w9ea95sd6ater"
target_pattern = r"[4-9]"
print(re.findall(target_pattern, str_1))
# ['6', '7', '8', '9', '9', '5', '6']
留神: 在这里的连接符 -
是示意范畴的,如果只是想匹配 -
字符,须要应用 \
进行本义,或者将 -
放在首位或者末位:
str_1 = "asd136q-78w-9e-a95sd6zater"
target_pattern = r"[a\-z]"
print(re.findall(target_pattern, str_1))
# ['a', '-', '-', '-', 'a', 'z', 'a']
下面的这个操作示意的是心愿匹配上 -
a
和 z
三个字符。
3) 特殊字符在汇合里只会匹配其原始字符含意
特殊字符,比方后面示意数量的 *
+
等字符在中括号里就匹配的是对应的星号和加号:
str_1 = "asdas*adas+asds(das)dasd"
target_pattern = r"[*+()]"
print(re.findall(target_pattern, str_1))
# ['*', '+', '(', ')']
4) 字符类 \s 或 \w 能够在汇合里应用
比方上面的操作,能够 \W 和 0-9 之间的字符:
str_1 = "asdas*adas+asds(90123das)dasd"
target_pattern = r"[\W0-9]"
print(re.findall(target_pattern, str_1))
# ['*', '+', '(', '9', '0', '1', '2', '3', ')']
5) 取反操作能够应用 ^
如果要取反,意思是汇合里的匹配模式都不匹配,比方咱们想匹配字符串里的非数字,能够如下操作:
str_1 = "asdas*adas+asds(90123das)dasd"
target_pattern = r"[^\d]"
print(re.findall(target_pattern, str_1))
# ['a', 's', 'd', 'a', 's', '*', 'a', 'd', 'a', 's', 'a', 's', 'd', 's', '(', 'd', 'a', 's', ')', 'd', 'a', 's', 'd']
5. 其余匹配类型
1) |- 表达式的或操作
比方咱们有两个表达式 A 和 B,只有有一个符合条件即可,即匹配模式的或操作:
re.findall(r"\d+|[a-z]+", "asdas234asd2341")
# ['asdas', '234', 'asd', '2341']
2) ()- 匹配括号内的任意正则表达式
小括号示意分组,能够将匹配模式分组放到多个小括号内进行匹配,匹配后的后果也会以元组的模式分组返回。
比方咱们想匹配 英文字母 + 数字
的模式,并且以括号的模式将其分隔开,那么返回的匹配后果也会以元组的模式将其返回:
str_1 = "asdas9872"
target_pattern = r"([a-z]+)(\d+)"
result = re.findall(target_pattern, str_1)print(result)
# [('asdas', '9872')]
如果是匹配上了多个后果,那么多个后果会以列表的模式返回,元素也是匹配的后果以元组的模式返回:
str_1 = "asdas9872asdasklqw8267"
target_pattern = r"([a-z]+)(\d+)"
result = re.findall(target_pattern, str_1)print(result)
# [('asdas', '9872'), ('asdasklqw', '8267')]
3、罕用正则办法
后面介绍了 re.findall() 办法,返回的是一个列表,元素是所有匹配上的后果,接下来介绍一些其余罕用的正则办法。
1. re.search
re.search() 办法的作用是扫描整个指标字符串,找到匹配上的第一个地位,而后返回一个匹配对象,如果没有满足要求的数据,则返回 None。
这里要留神三点,一点是扫描整个字符串,直到找到匹配的对象,另一点是找到一个符合条件的字符串当前就进行扫描,即使前面还有符合条件的,三是返回一个匹配对象,对于匹配对象上面再介绍。
比方上面的示例是匹配 英文字母 + 数字:
str_1 = "..()-+/?asdas9872asdasklqw8267"
target_pattern = r"[a-z]+\d+"
result = re.search(target_pattern, str_1)
# <re.Match object; span=(8, 17), match='asdas9872'>
能够看到这里,其实有多个合乎匹配模式的数据,如果这里应用 re.findall() 会返回多个值,但这里返回的只是字符串里第一个符合条件的数据。
至于如何获取返回的这个 re.Match 对象详情数据,见第四节 re.Match 匹配对象
,倡议先浏览该章节再往后读。
2. re.match
re.match() 办法也是用于匹配指定模式,成果上与 re.search() 无异,惟一不同的一点是 match() 办法只能从字符串的头部开始匹配,返回的也是一个 re.Match 对象。
比方上面的操作,第一种模式匹配不到数据,返回 None,第二种就能够返回 re.Match 对象:
str_1 = "..()-+/?asdas9872asdasklqw8267"
target_pattern = r"[a-z]+\d+"
result = re.match(target_pattern, str_1)
# None
str_1 = "asdas9872asdasklqw8267"
target_pattern = r"[a-z]+\d+"
result = re.match(target_pattern, str_1)
# <re.Match object; span=(0, 9), match='asdas9872'>
3. re.split
依据匹配模式将指定字符串进行宰割,在成果上相当于 string.split() 的增强版。
因为 string.split() 不能应用正则对象来对字符串进行切割,而 re.split() 能够实现。
比方咱们想要依据 1,2,3 对指定字符串进行切割,就能够用到 |
这个操作:
re.split(r"1|2|3", "as2da1s3asdsa")
# ['as', 'da', 's', 'asdsa']
咱们也能够应用其余正则对象,比方咱们想要依据字符串中的标点符号,空格等对字符串进行切割:
re.split(r"\W+", "i have an apple, you have two apples!")
# ['i', 'have', 'an', 'apple', 'you', 'have', 'two', 'apples', '']
re.split() 还能够承受 maxsplit 参数,示意最多切割的次数:
re.split(r"\W+", "i have an apple, you have two apples!", maxsplit=2)
# ['i', 'have', 'an apple, you have two apples!']
4. re.findall
re.findall() 后面有过介绍,依据匹配模式获取所有的匹配字符,后果以列表模式返回,元素为匹配的字符串:
re.findall(r"\d+", "asd12asxda45asd456sd23")
# ['12', '45', '456', '23']
5. re.finditer
re.finditer() 函数与 re.findall() 函数成果相似,都是找到指标字符串里全副满足条件的字符串,然而不同的是 finditer() 返回的一个迭代器,迭代器保留的是匹配对象,也就是 re.Match 对象:
for item in re.finditer(r"\d+", "asd12asxda45asd456sd23"):
print(item)
# <re.Match object; span=(3, 5), match='12'>
# <re.Match object; span=(10, 12), match='45'>
# <re.Match object; span=(15, 18), match='456'>
# <re.Match object; span=(20, 22), match='23'>
6. re.sub
替换函数,将字符串里的指定模式替换成指标字符串,而后返回:
re.sub(r"\d+", "***", "asd12asxda45asd456sd23")
# 'asd***asxda***asd***sd***'
7. re.subn
re.subn() 与 re.sub() 函数作用统一,都是替换指标字符串,然而返回的是一个元组,别离是替换后的字符串和替换的次数:
re.subn(r"\d+", "***", "asd12asxda45asd456sd23")
# ('asd***asxda***asd***sd***', 4)
8. re.compile
re.compile() 函数将正则表达式编译为一个正则表达式对象,而后能够调用后面介绍过的这些正则函数,比方 re.search(),re.match(),re.findall() 等。
pattern = re.compile(r"\d+")
pattern.match("3432asdas334asd34asd")
# <re.Match object; span=(0, 4), match='3432'>
re.complie() 这个函数的操作能够用于永福匹配模式,使程序更加高效。
9. 其余参数
接下来介绍一下正则模块在匹配状况下的一些其余参数。
1) re.I
疏忽大小写。
对于指标字符串,如果存在字母大小写的状况,咱们能够对原始字符串对立进行大写或者小写的操作,而后进行匹配,以疏忽大小写的影响,也能够应用 re.I 参数:
re.search(r"[a-z]+", "123SDAFSDsa234ASDd34".lower())
# <re.Match object; span=(3, 11), match='sdafsdsa'>
re.search(r"[a-z]+", "123SDAFSDsa234ASDd34", re.I)
# <re.Match object; span=(3, 11), match='SDAFSDsa'>
2) re.M
多行匹配模式。
后面介绍 ^
和 $
这两个符号的匹配的时候介绍过,如果加上 re.M 参数,示意的是能够匹配字符串外部有换行符的结尾和结尾的数据:
str_1 = "asd—— _\na 你‘' 好,\ns9。8?12\v3a\rs,.\ta9\n78d"target_pattern = r"^a"
print(re.findall(target_pattern, str_1, re.M))
# ['a', 'a']
4、re.Match 匹配对象
对于一些函数,比方后面介绍的 re.search(),返回的就是一个 re.Match 对象,还是以下面的示例为例,如何获取 re.Match 中的数据呢,上面开始介绍。
1. Match.group()
group() 函数间接调用,或者增加参数 0,返回的是匹配的字符串数据:
print(result.group())
# asdas9872
print(result.group(0))
# asdas9872
而如果咱们匹配的模式应用了小括号 ()
进行了分组,那么则能够对 group() 函数增加其余 index 示意匹配的分组的后果,比方上面的操作:
str_1 = "..()-+/?asdas9872asdasklqw8267"
target_pattern = r"([a-z]+)(\d+)"
result1 = re.search(target_pattern, str_1)
print(result1.group(0))
# asdas9872
print(result1.group(1))
# asdas
print(result1.group(2))
# 9872
2. Match.__getitem__(g)
re.Match 对象实现了 getitem 这个魔术办法,所以咱们能够通过索引的形式对来拜访匹配的后果,其成果和 group(index) 是统一的:
print(result1[0])
print(result1.group(0))
print(result1[2])
print(result1.group(2))
3. Match.groups()
groups() 函数返回的是进行了分组匹配的后果,以元组的模式返回。
比方这里分组的 result1 对象:
print(result1.groups())
# ('asdas', '9872')
而 result 这个 Match 对象匹配模式里并没有应用小括号进行分组,所以返回的后果是空元组:
print(result.groups())
# ()
4. Match.re
返回的是生成这个 Match 对象的正则对象,也就是咱们输出的 target_pattern:
print(result.re)
# re.compile('[a-z]+\\d+')
5. Match.string
返回的是传递到生成这个 Match 对象的原始字符串:
print(result.string)
# ..()-+/?asdas9872asdasklqw8267
6. Match.start() 和 Match.end()
这两个函数示意的是匹配上的字符串的开始地位和完结地位:
print(result.start(), result.end())
# 8 17
咱们对原始字符串进行起始地位的截取就是 Match.group() 的后果:
print(str_1[result.start(): result.end()])
# asdas9872
print(result.group())
# asdas9872
而如果咱们后面对匹配模式进行了分组的操作,也就是应用了小括号,比方返回的 result1,咱们能够想 start() 和 end() 函数增加索引参数别离示意这两个分组后果开始和完结的下标地位:
print(result1.groups())
# ('asdas', '9872')
print(str_1[result1.start(1): result1.end(1)])
# asdas
print(str_1[result1.start(2): result1.end(2)])
# 9872
7. Match.span()
返回的是匹配后果的开始和完结地位,以元组的模式返回,其实就是一次性返回 Match.start() 和 Match.end() 的后果。
span() 函数也能够承受分组的参数返回分组的起始地位。
如果想获取更多后端相干文章,可扫码关注浏览: