一、概念

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个规则字符串,这个规则字符串用来表达对字符串的一种过滤逻辑(可以用来做检索截取或者替换操作)。

二、正则表达式的使用

2.1 match()

re.match()尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回None。语法格式如下:

1
re.match(pattern, string, flags=0)
参数 描述
pattern 匹配的正则表达式
string 目标字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。如下表列出正则表达式修饰符 - 可选标志
修饰符 描述
re.I 是匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响^和$
re.S 使,匹配包括换行内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响\w,\W,\b,\B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写的更易于理解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import re
s = 'hello python'
pattern = 'hello'
v = re.match(pattern, s)
print(v)
print(dir(v))
print(v.group())
print(v.span()) #匹配范围
print(v.start())

#结果
<re.Match object; span=(0, 5), match='hello'>
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
hello
(0, 5)
0

s = 'hello python'
pattern = 'hello'
v = re.match(pattern, s, flag = re.I) #flag = re,I(不区分大小写)
print(v)
print(dir(v))
print(v.group())
print(v.span()) #匹配范围
print(v.start())

#结果
<re.Match object; span=(0, 5), match='hello'>
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
hello
(0, 5)
0

2.2 常用匹配符

正则表达式中常用的字符

符号 描述
. 匹配任意一个字符(除了\n)
[] 匹配列表中的字符
\w 匹配字母,数字,下划线,即a-z,A-Z,0-9, _
\W 匹配不是字母,数字,下划线
\s 匹配空白字符,即空格(\n,\t)
\S 匹配不是空白的字符
\d 匹配数字,即0-9
\D 匹配非数字的字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import re
print('-----.的使用--------')
pattern='.' #不能匹配换行符\n
# s='a'
# s='C'
# s='_'
s='\n'
v=re.match(pattern,s)
print(v)

print('-------\d 的使用--------')
pattern='\d'
# s='9'
# s='4'
# s='a'
# s='_'
v=re.match(pattern,s)
print(v)

print('-------\D 的使用--------')
pattern='\D'
s='9'
# s='4'
# s='a'
s='_'
v=re.match(pattern,s)
print(v)

print('-------\s 的使用--------')
pattern='\s'
s=' '
s='\t'
s='\n'
s='_'
v=re.match(pattern,s)
print(v)

print('-------\S 空白--------')
pattern='\S'
s=' '
s='\t'
s='\n'
s='_'
v=re.match(pattern,s)
print(v)

print('-------\w 的使用--------')
# pattern='\w'
pattern='\W'
s='a'
# s='_'
# s='5'
# s='A'
s='#'
v=re.match(pattern,s)
print(v)

print('-------[]的使用--------')
pattern='[2468]'
s='1'
s='2'
s='3'
s='4'
s='#'
v=re.match(pattern,s)
print(v)

2.3 表示数量

1
2
3
4
5
6
7
import re
print('---------手机号码-----------')
# pattern='\d\d\d\d\d\d\d\d\d\d\d'#匹配手机号
pattern='1[35789]\d\d\d\d\d\d\d\d\d'#匹配手机号
s='13456788789'
v=re.match(pattern,s)
print(v)

如果要匹配电话号码,需要形如\d\d\d\d\d\d\d\d\d\d\d这样的正则表达式。其中表现了 11 次\d,表达方式烦琐。而且某些地区的电话号码是 8 位数字,区号也有可能是 3位或 4 位数字,因此这个正则表达式就不能满足要求了。正则表达式作为一门小型的语言,还提供了对表达式的一部分进行重复处理的功能。例如,*可以对正则表达式的某个部分重复匹配多次。这种匹配符号称为限定符。下表列出了正则表达式中常用的限定符。

符号 描述 符号 描述
* 匹配零次或多次 {m} 重复m次
+ 匹配一次或多次 {m, n} 重复m到n次,其中n可以省略,表示m到任意次
? 匹配一次或零次 {m,} 至少m次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
print('------*的使用--------')
pattern='\d*' #0 次或多次
s='123abc'
s='abc' #这时候不是 None 而是'',因为 abc 前面默认有空
v=re.match(pattern,s)
print(v)
print('-----+的使用---------')
pattern='\d+' #1 次或多次
s='123abc'
s='abc' #这时候是 None
v=re.match(pattern,s)
print(v)

print('-----?的使用---------')
pattern='\d?' #0 次或 1 次
# s='123abc'
s='abc' #这时候是空
v=re.match(pattern,s)
print(v)

print('-----{m}的使用---------')
pattern='\d{3}' #出现 m 次
pattern='\d{2}' #出现 m 次
pattern='\d{4}' #出现 m 次
s='123abc'
v=re.match(pattern,s)
print(v)

print('-----{m,}的使用---------')
# pattern='\d{3,}' #出现大于 m 次 尽可能满足的都返回
pattern='\d{2,4}' #出现 m 到 n 次
s='1234567abc'
v=re.match(pattern,s)
print(v)
1
2
3
4
5
pattern='[A-Z][a-z]*' #A-Z表示A-Z的所有大写字母,这个牛
s='Hello world'
s='HEllo world'
v=re.match(pattern,s)
print(v)

三、边界字符

字符 功能
^ 匹配字符串开头
$ 匹配字符串结尾
\b 匹配一个单词的边界
\B 匹配非单词的边界

注意:^与[\^m]中的^的含义并不相同,后者^表示除了……的意思

3.1 $的使用

1
2
3
4
5
6
7
8
9
10
import re
#匹配 qq 邮箱, 5-10 位
pattern = '[\d]{5,10}@qq.com'
#必须限制结尾的
# pattern = '[1-9]\d{4,9}@qq.com$'
#正确的地址
v = re.match(pattern,'12345@qq.com')
#未限制结尾的前提下使用不正确的地址
# v = re.match(pattern,'12345@qq.comabc')
print(v)

3.2 ^的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re
pattern = r'^hello\s.*'
s1 = 'hello python'
s2 = 'hello world'
s3 = 'hi python'
s4 = 'hello2 python'
o1 = re.match(pattern, s1)
o2 = re.match(pattern, s2)
o3 = re.match(pattern, s3)
o4 = re.match(pattern, s4)
print(o1)
print(o2)
print(o3)
print(o4)

#结果
<re.Match object; span=(0, 12), match='hello python'>
<re.Match object; span=(0, 11), match='hello world'>
None
None

3.3 \b的使用

1
2
3
4
5
6
7
8
9
10
import re
pattern = r'.*\bab'
#ab 左边界的情况
v = re.match(pattern,'123 abr')
print(v)

pattern = r'.*ab\b'
#ab 为右边界的情况
v = re.match(pattern,'wab')
print(v)

3.4 \B的使用

1
2
3
4
5
6
7
8
9
import re
#ab 不为左边界
pattern = r'.*\Bab'
v = re.match(pattern,'123 abr')
print(v)
#ab 不为右边界
pattern = r'.*ab\B'
v = re.match(pattern,'wab')
print(v)

四、Search()方法

Search()的使用与match()方法一样,语法格式相同,但是Search()方法匹配不是从字符串的开头而是匹配整个字符串。

1
2
3
4
import re
m=re.search('abc','abcdefg')
print(m)
print(m.group())
1
2
3
4
5
6
7
8
9
10
11
import re
#进行文本模式匹配,匹配失败,match 方法返回 None
m=re.match('love','I love you') #这里m返回的是none
if m is not None:
print(m.group())
print('match 运行结果:',m)
#进行文本模式搜索,
m=re.search('love','I love you') #这里m返回的查找对象不是none
if m is not None:
print(m.group())
print('search 的运行结果:',m)

五、匹配多个字符串

search 方法搜索一个字符串,要想搜索多个字符串,如搜索 aa、bb 和 cc,最简单的方法是在文本模式字符串中使用择一匹配符号(|)。择一匹配符号和逻辑或类似,只要满足任何一个,就算匹配成功

1
2
3
4
5
6
7
8
9
10
import re
s='aa|bb|cc'
#match 进行匹配
m=re.match(s,'aa') #aa 满足要求,匹配成功
print(m.group())
m=re.match(s,'bb') #bb 满足要求,匹配成功
print(m.group())
#search 查找
m=re.search(s,'Where is cc')
print(m.group())

匹配0-100之间的数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import re
pattern = r'0|100|[1-9]\d?$'
s1 = '0'
s2 = '100'
s3 = '96'
s4 = '-1'
s5 = '101'
o1 = re.match(pattern, s1)
o2 = re.match(pattern, s2)
o3 = re.match(pattern, s3)
o4 = re.match(pattern, s4)
o5 = re.match(pattern, s5)
print(o1)
print(o2)
print(o3)
print(o4)
print(o5)

#结果
<re.Match object; span=(0, 1), match='0'>
<re.Match object; span=(0, 3), match='100'>
<re.Match object; span=(0, 2), match='96'>
None
None

5.1 择一匹配符和列表的区别

如果待匹配的字符串中,某些字符可以有多个选择,就需要使用字符集([]),也就是一对中括号括起来的字符串。例如,[xyz]表示 x、y、z 三个字符可以取其中任何一个,相当于“x|y|z”,所以对单个字符使用或关系时,字符集和择一匹配符的效果是一样的。示例如下:

1
2
3
4
5
import re
m=re.match('[xyz]','x') #匹配成功
print(m.group())
m=re.match('x|y|z','x') #匹配成功
print(m.group())
1
2
3
4
5
6
7
8
9
10
import re
#匹配以第 1 个字母是 a 或者 b,第 2 个字母是 c 或者 d,如 ac、bc、ad、bd
m=re.match('[ab][cd]','aceg')
print(m)
#匹配以 ab 开头,第 3 个字母是 c 或者 d,如 abc、abd
m=re.match('ab[cd]','abcd')
print(m)
#匹配 ab 或者 cd
m=re.match('ab|cd','cd')
print(m)

六、分组

如果一个模式字符串中有用一对圆括号括起来的部分,那么这部分就会作为一组,可以通过 group 方法的参数获取指定的组匹配的字符串。当然,如果模式字符串中没有任何用圆括号括起来的部分,那么就不会对待匹配的字符串进行分组。

字符 功能
(a,b) 将括号中的字符作为一个分组
\num 引用分组num匹配到的字符串
(?P\) 分别其组名
(?P=name) 引用别名为name分组匹配到的字符串

6.1 示例

匹配电话号码距离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pattern = r'(\d+)-(\d{5,8}$)'
v = re.match(pattern,'010-66668888')
print(v)
print(v.group())
print(v.group(1))
print(v.group(2))
print(v.groups()) #元组
print(v.groups()[0])
print(v.groups()[1])

#结果
<re.Match object; span=(0, 12), match='010-66668888'>
010-66668888
010
66668888
('010', '66668888')
010
66668888

6.2 \num使用

1
2
3
4
5
6
7
8
#匹配出网页标签内的数据
s = '<html><title>我是标题</title></html>'
#优化前
# pattern = r'<.+><.+>.+</.+></.+>'
#优化后 可以使用分组 \2 表示引用第 2 个分组 \1 表示引用第 1 个分组
pattern = r'<(.+)><(.+)>.+</\2></\1>' #从一开始
v = re.match(pattern,s)
print(v)

6.3 (?P\)使用

1
2
3
4
5
6
7
s = '<html><h1>我是一号字体</h1></html>'
# pattern = r'<(.+)><(.+)>.+</\2></\1>'
#如果分组比较多的话,数起来比较麻烦,可以使用起别名的方法?P<要起的名字> 以及使
用别名(?P=之前起的别名)
pattern = r'<(?P<key1>.+)><(?P<key2>.+)>.+</(?P=key2)></(?P=key1)>'
v = re.match(pattern,s)
print(v)

注意:

  1. 只有圆括号括起来的部分才算一组,如果模式字符串中既有圆括号括起来的部分也有没有被圆括号括起来的部分,那么只会将被圆括号括起来的部分算作一组,其它的部分忽略;
  2. 用 group 方法获取指定组的值时,组从 1 开始,也就是说,group(1)获取第 1 组的值,group(2)获取第 2 组的值,以此类推;
  3. groups 方法用于获取所有组的值,以元组形式返回。所以除了使用 group(1)获取第1 组的值外,还可以使用 groups()[0]获取第 1 组的值。获取第 2 组以及其它组的值的方式类似。

七、其他常用函数

7.1 sub和subn搜索和替换

sub函数和subn函数用于实现搜索和替换功能。这两个函数的功能几乎完全相同,都是将某个字符串中所有匹配正则表达式的部分替换成其他字符串。用来替换的部分可能是一个字符串,也可以是一个函数,该函数返回一个用来替换的字符串。sub函数返回替换后的结果subn 函数返回一个元组,元组的第 1 个元素是替换后的结果,第 2 个元素是替换的总数。语法格式如下:

1
re.sub(pattern, repl, string, count=0, flags=0)
参数 描述
pattern 匹配的正则表达式
repl 替换的字符串,也可以为一个函数
string 要被查找替换的原始字符串
count 模式匹配后替换的最大次数,默认0表示替换所有的匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import re
phone = "2004-959-559 # 这是一个国外电话号码"
# 删除字符串中的 Python 注释
num = re.sub(r'#.*$', "", phone)
print("电话号码是: ", num)
# 删除非数字(-)的字符串
num = re.sub(r'\D', "", phone)
print("电话号码是 : ", num)
#subn 函数的使用
result=re.subn(r'\D', "", phone)
print(result)
print('替换的结果:',result[0])
print('替换的次数:',result[1])

#结果
电话号码是: 2004-959-559
电话号码是 : 2004959559
('2004959559', 15)
替换的结果: 2004959559
替换的次数: 15

7.2 compile()函数

compile()函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match()和 search() 这两个函数使用。语法格式为:

1
re.compile(pattern[, flags])
参数 描述
pattern 一个字符串形式的正则表达式
flags 可选,表示匹配模式,比如忽略大小写,多行模式等
1
2
3
4
5
6
7
8
9
10
11
12
import re
s='first123 line'
regex=re.compile(r'\w+') #匹配至少一个字母或数字
m=regex.match(s)
print(m.group())
# s 的开头是 "f", 但正则中限制了开始为 i 所以匹配失败
regex = re.compile("^i\w+")
print(regex.match(s))

# 结果
first123
None

7.3 findall()函数

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。语法格式如下:

1
findall(pattern, string, flags=0)
参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串
flags 可选,表示匹配模式,比如忽略大小写,多行模式等
1
2
3
4
pattern=r'\w+'
s='first 1 second 2 third 3'
o=re.findall(pattern,s)
print(o)

7.4 finditer()函数

和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pattern=r'\w+'
s='first 1 second 2 third 3'
o=re.finditer(pattern,s)
print(o)
for i in o:
print(i.group())
#结果
<callable_iterator object at 0x7fb2ca184ad0>
first
1
second
2
third
3

7.5 split()函数

split 函数用于根据正则表达式分隔字符串,也就是说,将字符串与模式匹配的子字符串都作为分隔符来分隔这个字符串。split 函数返回一个列表形式的分隔结果,每一个列表元素都是分隔的子字符串。语法格式如下:

1
re.split(pattern, string[, maxsplit=0, flags=0])
参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串
maxsplit 分隔次数,maxsplit=1分隔一次,默认为0, 不限制次数
flags 可选,表示匹配模式,比如忽略大小写,多行模式等
1
2
3
4
5
6
7
8
9
10
import re
s='first 11 second 22 third 33'
#按数字切分
print(re.split(r'\d+',s))
# maxsplit 参数限定分隔的次数,这里限定为 1,也就是只分隔一次
print(re.split(r'\d+',s,1))

#结果
['first ', ' second ', ' third ', '']
['first ', ' second 22 third 33']

八、贪婪与非贪婪

贪婪模式指Python 里数量词默认是贪婪的,总是尝试匹配尽可能多的字符。非贪婪模式与贪婪相反,总是尝试匹配尽可能少的字符,可以使用”*”,”?”,”+”,”{m,n}”后面加上?,使贪婪变成非贪婪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import re
v = re.match(r'(.+)(\d+-\d+-\d+)','This is my tel:133-1234-1234')
print('----------贪婪模式---------')
print(v.group(1))
print(v.group(2))
print('----------非贪婪模式---------')
v = re.match(r'(.+?)(\d+-\d+-\d+)','This is my tel:133-1234-1234')
print(v.group(1))
print(v.group(2))

# 结果
----------贪婪模式---------
This is my tel:13
3-1234-1234
----------非贪婪模式---------
This is my tel:
133-1234-1234
1
2
3
4
5
6
7
8
9
10
11
12
import re
print('贪婪模式')
v= re.match(r'abc(\d+)','abc123')
print(v.group(1))
#非贪婪模式
print('非贪婪模式')
v= re.match(r'abc(\d+?)','abc123')

#结果
贪婪模式
123
非贪婪模式