Python正则

Posted by Wh0ami-hy on July 1, 2021

1. 定义

正则表达式是一个特殊的字符序列,通常被用来检索或替换那些符合某个规则的字符串

2. 正则表达式模式

2.1. 规则

  • 数字和字母都表示本身
  • 有的字母前加\有特殊含义(重点)
a = re.search(r'\d','he12ms90') # 这⾥的 \d 表示的是匹配数字
  • 大多数标点符号都有特殊含义(重点)
  • 要匹配标点符号需要在前面加\
a = re.search(r'\.','he.llo') # 这⾥的 \. 进⾏了转义,才表示标点符号自身

2.2. 分组

正则表达式里使用()来表示一个分组

ret = re.findall(r'\w+@(qq|126|163)\.com','123@qq.com;aa@163.com;bb@126.com')
print(ret) 
#结果为 ['qq', '163', '126'] 只匹配到了了分组里的内容

2.3. 模式(通用)

2.3.1. 非打印字符

非打印字符,即在程序中不显示的字符,如换行符\n

非打印字符 描述
\cx 匹配由x指明的控制字符,例如, \cM 匹配一个 Control-M 或 回车符。x 的值必须为 A-Z 或 a-z之一。否则,将 c 视为一个原义的 c字符
\f 匹配一个换页符。等价于 \x0c\cL
\n 匹配一个换行符。等价于 \x0a\cJ
\r 匹配一个回车符。等价于 \x0d\cM
\s 匹配任何非打印字符(空白字符),包括空格、制表符、换页符等。等价于 [ \f\n\r\t\v]。注意 Unicode 正则表达式会匹配全角空格符
\S 匹配任何非空白字符。等价于[^ \f\n\r\t\v]
\t 匹配一个制表符。等价于 \x09\cI
\v 匹配一个垂直制表符。等价于 \x0b\cK

2.3.2. 特殊字符

有特殊含义的字符。若要匹配这些特殊字符,必须首先使字符转义,即将反斜杠字符\放在它们前面

特殊字符 描述
() 使用 \(\)
. 匹配除换行符 \n 之外的任何单字符。要匹配 .\.
[ 表示可选项的范围,[x-y]表示匹配x到y中的一个(包含x、y)。要匹配 [\[
\ 使用原生字符串使用两个\即可匹配一个\
{ 限定前面元素出现的次数。要匹配 {\{
| 表示或者。要匹配||
\d 匹配一个数字字符。等价于 [0-9]
[0-9] 匹配任何数字。等价于 \d
\D 匹配一个非数字字符。等价于 [^0-9]
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任何字母及数字。等价于\w
\w 匹配包括下划线的任何单词字符。等价于[a-zA-Z0-9]
\W 匹配任何非单词字符。等价于 [^a-zA-Z0-9]
[\u4e00-\u9fa5] 匹配纯中文

2.3.3. 定位符

将正则表达式固定到行首或行尾。它能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾

定位符 描述
^ 以指定内容开头,如^a表示以a开头的内容,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 \^
$ 指定内容结尾,如a$表示以a结尾的内容
\b 匹配一个单词边界,即字与空格间的位置
\B 非单词边界匹配

详解\b

  • 什么是位置

It’s a nice day today.

‘I’占一个位置,’t’ 占一个位置,所有的单个字符(包括不可见的空白字符)都会占一个位置,这样的位置我给它取个名字叫“显式位置”。

注意:字符与字符之间还有一个位置,例如 ‘I’ 和 ‘t’ 之间就有一个位置(没有任何东西),这样的位置我给它取个名字叫“隐式位置”。

\b 就是“隐式位置”

  • \b

就用 “It’s a nice day today.” 举例说明:

正确的正则:\bnice\b

分析:第一个 \b 前面一个字符是空格,后面一个字符是 ‘n’,不全是 \w,所以可以匹配出 ‘n’ 是一个单词的开头。第二个 \b 前面一个字符是 ‘e’,后面一个字符是空格,不全是 \w,可以匹配出 ‘e’ 是一个单词的结尾。所以,合在一起,就能匹配出以 ‘n’ 开头以 ‘e’ 结尾的单词,这里就能匹配出 “nice” 这个单词。

2.3.4. 限定符

用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配

限定符 描述
* 匹配前面的子表达式零次或多次。例如,zo* 能匹配 z 以及 zoo* 等价于{0,}
+ 匹配前面的子表达式一次或多次。例如,zo+ 能匹配 zo 以及zoo,但不能匹配 z+ 等价于 {1,}
? 匹配前面的子表达式零次或一次。例如,do(es)? 可以匹配 dodoes中的 doesdoxy 中的do?等价于 {0,1}
{n} n 是一个非负整数。匹配前面的子表达式确定的 n 次。例如,o{2} 不能匹配Bob中的 o,但是能匹配food中的两个o
{n,} n 是一个非负整数。至少匹配前面的子表达式 n 次。例如,o{2,} 不能匹配 Bob 中的 o,但能匹配 foooood中的所有 o{1,} 等价于 o+o{0,} 则等价于 o*
{,n} 表示前面的元素出现 n 次以下
{n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。。例如,o{1,3} 将匹配fooooood中的前三个oo{0,1} 等价于 o?

3. 匹配\

  • 需要使用四个\来匹配一个\
  • 在Python 字符串前面添加r 即可将字符串转换成为原生字符串,通过原生字符串使用两个\即可匹配一个\

4. re之查找

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等

4.1. re.match

  • 只对字符串查询一次
  • 只匹配字符串开头,如果不是起始位置匹配成功的话,match()就返回none
  • 返回值的类型为re.Match类型的对象
  • 使用group(num)函数来获取匹配的字符串结果

语法

re.match(pattern,string,flags=0)

例子

import re
x = 'helloword'

a = re.match(r'H',x)
b = re.match(r'h',x)
print(a)	#结果为 None
print(b)	#结果为 <re.Match object; span=(0, 1), match='h'>

4.2. re.search

  • 只对字符串查询一次
  • 扫描整个字符串,并返回第一个成功的匹配
  • 返回值的类型为re.Match类型的对象
  • 使用group(num)函数来获取匹配的字符串结果

语法

re.search(pattern,string,flags=0)

例子

import re
x = 'jaskldjklasjpdf'

a = re.search(r'a',x)

print(a)	#结果为 <re.Match object; span=(1, 2), match='a'>
			#只返回第一个成功的匹配

4.3. re.finditer

和findall类似,但是re.finditer更适合当你需要匹配的详细信息或处理大文本时,finditer是按需生成匹配对象,不会一次性将所有匹配存入内存

  • 扫描整个字符串,找到所有的匹配
  • 返回一个可迭代对象
  • 返回值的类型为re.Match类型的对象
  • 使用group(num)函数来获取匹配的字符串结果

语法

re.finditer(pattern,string,flags=0)

例子

import re
x = 'jaskldjklasjpdf'

a = re.finditer(r'a',x)

for i in a:
    print(i)
    
#结果是 <re.Match object; span=(1, 2), match='a'>
	  #<re.Match object; span=(9, 10), match='a'>

4.4. re.findall

  • 扫描整个字符串,找到所有的匹配
  • 返回一个列表
  • 如果没有找到匹配的,则返回空列表

注意

如果匹配规则里有分组,则只匹配分组数据

ret = re.findall(r'\w+@(qq|126|163)\.com','123@qq.com;aa@163.com;bb@126.com')
print(ret) 
#结果为 ['qq', '163', '126'] 只匹配到了了分组里的内容

如果匹配规则里存在多个分组,则会把多个分组匹配成元组

ret = re.findall(r'\w+@(qq|126|163)(\.com)','123@qq.com;aa@163.com;bb@126.com'
)
print(ret) 
#结果为 [('qq', '.com'), ('163', '.com'), ('126', '.com')]

如果想要让findall匹配所有的内容,而不仅只是匹配正则表达式里的分组,可以使用 ?:来将分组标记为非捕获分组

ret = re.findall(r'\w+@(?:qq|126|163)\.com','123@qq.com;aa@163.com;bb@126.com'
)
print(ret) 
#结果为 ['123@qq.com', 'aa@163.com', 'bb@126.com']

语法

re.findall(pattern,string,flags=0)

例子

import re
x = 'jaskl9lasj'
a = re.findall(r'a',x)
print(a)
#结果为 ['a', 'a']

4.5. re.fullmatch

  • 检查整个字符串是否完全匹配给定的正则表达式模式。如果字符串与模式完全一致,则返回一个 Match 对象,否则返回 None。这个方法适合用于需要验证整个输入格式的场景,比如输入校验或数据验证
  • 使用group(num)函数来获取匹配的字符串结果

语法

re.fullmatch(pattern,string,flags=0)

例子

import re
x = 'jaskl9lasj'

a = re.fullmatch(r'jask',x)
b = re.fullmatch(r'j.*j',x)

print(a)	#结果为 None
print(b)	#结果为 <re.Match object; span=(0, 10), match='jaskl9lasj'>

4.6. re.Match类

调用re.match 方法、re.search 方法、re.fullmatch方法,或者对re.finditer 方法的结果进行迭代时,拿到的数据类型都是re.Match 对象,这个类里定义了相关的属性

属性和方法 说 明
pos 搜 索 的 开 始 位 置
endpos 搜 索 的 结 束 位 置
string 搜 索 的 字 符 串
re 当 前 使 用 的 正 则 表 达 式 的 对 象
lastindex 最 后 匹 配 的 组 索 引
lastgroup 最 后 匹 配 的 组 名
group(index=0) 某个分组的匹配结果。如果index等于0,便是匹配整个正则表达式
groups() 所有分组的匹配结果,每个分组的结果组成一个元组返回
groupdict() 返回组名作为key,每个分组的匹配结果做为value的字典
start([group]) 获 取 组 的 开 始 位 置
end([group]) 获 取 组 的 结 束 位 置
span([group]) 获 取 组 的 开 始 和 结 束 位 置
expand(template) 使用组的匹配结果来替换模板template中的内容,并把替换后的字符串返回

group()

group()方法表示正则表达式的分组。group(n)来表示第 n 个分组,第 0 组就是把整个正则表达式当做整体。没有分组,则默认只有一组。分组的下标从 0 开始

import re
x = 'jaskl9laasd5sad4123as1231x2j'

a = re.search(r'(j.*)(9.*)(4.*)',x) # 分3组

print(a)
print(a.group(0))	# 0 组默认是把整个正则表达式当做整体 
print(a.group(1))	# 1 组 
print(a.group(2))	# 2 组
print(a.group(3))	# 3 组

groupdict()

groupdict() 返回组名作为key,每个分组的匹配结果做为value的字典

为分组起名字 ?P<name>

import re
x = 'jaskl9laasd5sad4123as1231x2j'

a = re.search(r'(?P<name1>j.*)(?P<name2>9.*)(?P<name3>4.*)',x)

print(a.groupdict())

5. re.compile方法

  • 使用正则表达式时,可以直接调用re 模块的 match、search、findall 等方法,传入指定的正则表达式
  • 也可以调用re.compile方法,生成一个正则表达式对象,再调用这个正则表达式对象的相关方法实现匹配
import re
# 1 
y = 'jaskl9laasd5sad4123as1231x2j'
r = re.compile(r'j.*')
b = r.search(y)
print(b)
# 2 
y = 'jaskl9laasd5sad4123as1231x2j'
b = re.search(r'j.*',y)
print(b)
# 1 和 2 等价

6. 正则表达式修饰符

修饰符 描述
re.I(大写i) 使匹配对大小写不敏感
re.M 多行匹配,影响 ^$
re.S 使 . 匹配包括换行在内的所有字符

re.I

import re
x = 'ABC'
b = re.search(r'a.*',x)
c = re.search(r'a.*',x,re.I)
print(b)	#结果为 None
print(c)	#结果为 <re.Match object; span=(0, 3), match='ABC'>

re.M

import re
y = 'I like eat food\nShe do not like\nShe love me'

b = re.findall(r'\w+$',y)
c = re.findall(r'\w+$',y,re.M)
print(b)	#结果为 ['me']
print(c)	#结果为 ['food', 'like', 'me']

re.S

import re
y = 'ajakl9l\nad5'
b = re.search(r'9.*',y)
c = re.search(r'9.*',y,re.S)
print(b)	#结果为 <re.Match object; span=(5, 7), match='9l'>
print(c)	#结果为 <re.Match object; span=(5, 11), match='9l\nad5'>

7. 正则替换

使用 re.sub 替换字符串中的匹配项

7.1. 语法

re.sub(pattern,repl,string,count=0)

7.2. 参数

参数 描述
pattern 正则中的模式字符串
repl 替换的字符串,也可为一个函数
string 要被查找替换的原始字符串
count 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配

repl

当repl传入的是一个函数test()时,re.sub内部会调用该函数,并把每一个匹配到的数据以re.Match的格式传参,传给函数test()

import re
y = 'I like eat123food,She do888not like,She love me'

def test(x):
    y = int(x.group(0))
    y *= 2
    return str(y)
b = re.sub(r'\d',test,y)

print(b)

7.3. 例子

import re
y = 'I like eat123food,She do888not like,She love me'

b = re.sub(r'\d','x',y)
c = re.sub(r'\d+','x',y)
print(b)	#结果为 I like eatxxxfood,She doxxxnot like,She love me
print(c)	#结果为 I like eatxfood,She doxnot like,She love me

8. 贪婪和非贪婪

  • Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符
  • 非贪婪则相反,总是尝试匹配尽可能少的字符
  • *?+{m,n} 后面加上 ? 使贪婪变成非贪婪
import re

y = 'm1234'
b = re.search(r'm\d+?',y)
c = re.search(r'm\d+',y)

print(b)	#结果为 <re.Match object; span=(0, 2), match='m1'>
print(c)	#结果为 <re.Match object; span=(0, 5), match='m1234'>

本站总访问量