关于后端:Python笔记五之正则表达式

41次阅读

共计 11150 个字符,预计需要花费 28 分钟才能阅读完成。

本文首发于公众号:Hunter 后端

原文链接:Python 笔记五之正则表达式

这一篇笔记介绍在 Python 里应用正则表达式。

正则表达式,Regular Expression,可用于在一个指标字符串里对于指定模式的字符进行查找、替换、宰割等操作。

比方,判断某个字符串里是否都是数字,或者是否蕴含指定字符串,又或者更间接的例子是判断电话号码或者邮箱是否非法等。

这一篇笔记里,咱们将先介绍一个正则表达式的函数,并以此来引入正则表达式的各种模式,并辅以各个例子进行介绍。

u1s1,正则表达式,我学了三四遍,学一遍忘一遍,忘一遍学一遍,只有隔一阵子不必就会忘,所以这一篇笔记力求将正则表达式的所有模式和用法都记录下来,用作之后的查找笔记。

以下笔记应用 Python 版本是 3.8。

以下是本篇笔记目录:

  1. re.findall
  2. 表达式语法

    1. 匹配字符串类型

      1. \b- 匹配空字符串,但只在单词开始或结尾
      2. \B- 匹配空字符串,不能在结尾或结尾
      3. \d- 匹配十进制数字
      4. \D- 匹配非十进制字符
      5. \s- 匹配空白字符
      6. \S- 匹配非空白字符
      7. \w- 匹配字符
      8. \W- 匹配非单词字符
    2. 匹配字符串呈现地位
      1) \A- 只匹配字符串开始
      2) \Z- 只匹配字符串结尾
      3) ^- 匹配字符串结尾
      4) $- 匹配字符串结尾
    3. 匹配字符串数量
      1) *- 匹配反复 0 到 n 次
      2) +- 匹配反复 1 到 n 次
      3) ?- 匹配反复 0 到 1 次
      4) {}- 指定匹配反复次数

       a. {m}- 只匹配反复 m 次
       b. {m,n}- 匹配反复 m 到 n 次 
    4. 匹配字符串汇合
      1) 字符能够独自列出
      2) 能够示意字符范畴
      3) 特殊字符在汇合里只会匹配其原始字符含意
      4) 字符类 \s 或 \w 能够在汇合里应用
      5) 取反操作能够应用 ^
    5. 其余匹配类型

      1. |- 表达式的或操作
      2. ()- 匹配括号内的任意正则表达式
  3. 罕用正则办法

    1. re.search
    2. re.match
    3. re.split
    4. re.findall
    5. re.finditer
    6. re.sub
    7. re.subn
    8. re.compile
    9. 其余参数
      1) re.I
      2) re.M
  4. re.Match 匹配对象

    1. Match.group()
    2. Match.__getitem__(g)
    3. Match.groups()
    4. Match.re
    5. Match.string
    6. Match.start() 和 Match.end()
    7. 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 字符,比方心愿 aababbabbbb 等都能够被匹配上。

那么就能够应用上面的操作:

str_1 = "axxxxabbxxxxabbbb"
target_pattern = r"ab*"
print(re.findall(target_pattern, str_1))
# ['a', 'abb', 'abbbb']

2) +- 匹配反复 1 到 n 次

+ 示意匹配的数量能够反复从 1 到 n 次,提前条件是匹配模式必须呈现一个。

还是下面的例子,咱们心愿能够匹配上 ababbb,以及有限多个 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']

下面的这个操作示意的是心愿匹配上 - az 三个字符。

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() 函数也能够承受分组的参数返回分组的起始地位。

如果想获取更多后端相干文章,可扫码关注浏览:

正文完
 0