简介
随着科学技术的快速发展,整个社会的各行各业产生了大量的数据。比如数据库中的信息流、金融市场的股票报价、公司的财务报表,新闻内容,计算机程序等。我们想要人工处理这么多的数据变得越来越不可能。这就需要计算机来批量处理这些数据,所以将数据筛选和处理的模式告诉计算机就特别重要了,于是就诞生了正则表达式。它是一些由字符和特殊字符组成的字符串,不同的字符组合能产生不同的匹配模式,通过调用正则表达式函数能够实现这些匹配规则。
正则表达式符号
在正式开始介绍正则表达式之前,我们首先介绍下相关的符号,以备查阅:
符号 | 含义 |
---|---|
\n | 匹配一个换行符 |
\t | 匹配一个制表符 |
\w | 匹配任意一个字母、数字或下划线 |
\W | 匹配除字母、数字和下划线以外的任意一个字符 |
\d | 匹配任意一个十进制数 |
\D | 匹配除十进制数外的任意一个字符 |
\s | 匹配任意一个空白字符 |
\S | 匹配除空白字符以外的任意一个字符 |
. | 匹配除换行符以外的任意一个字符 |
^ | 匹配字符串的开始位置 |
$ | 匹配字符串的结束位置 |
* | 匹配0次、1次或多次前面的原子 |
? | 匹配0次或1次前面的原子 |
+ | 匹配1次或多次前面的原子 |
{n} | 前面的原子恰好出现n次 |
{n,} | 前面的原子至少出现n次 |
{n,m} | 前面的原子至少出现n次,至多出现m次 |
| | 模式选择符或 |
() | 模式单元 |
I | 匹配时忽略大小写 |
M | 多行匹配 |
L | 做本地化识别匹配 |
U | 根据Unicode字符及解析字符 |
S | 匹配任意字符 |
这些常用的符号可以分成三类:
- 原子
- 元字符
- 模式修正符
接下来会一一作介绍。
原子
正则表达式本身只是一串字符串,没有任何功能。其通过正则表达式函数实现相关的匹配和筛选功能。在这里我们首先介绍一个常用的正则表达式函数search()
函数。该函数的使用格式为:
1 2 |
import re re.search(正则表达式, 源字符串) |
接下来我们介绍第一种正则表达式:原子。
原子是正则表达式中最基本的单位,常见的原子类型有以下几种:
- 普通字符;
- 非打印字符;
- 通用字符;
- 原子表。
普通字符:
首先介绍普通字符作为匹配模式的情况,假设现在有一串字符"my name is finthon!"
,我们需要将"finthon"
筛选出来,代码如下:
1 2 3 4 5 6 |
import re a = "my name is finthon!" b = "finthon" x = re.search(b, a) print(x) print(x.group()) |
结果为:
1 2 |
<_sre.SRE_Match object; span=(11, 18), match='finthon'> finthon |
可以看出,我们使用search()
函数按照匹配模式b
去匹配字符串a
中的元素,这里b
就是由普通字符构成。输出的结果x
是一个匹配对象,这个对象说明了匹配的范围在(11, 18)
字符之间,匹配的结果为'finthon'
;接着调用对象的group()
方法能够将最终的匹配结果输出出来。
非打印字符:
接下来介绍非打印字符作为原子的情况。常见的非打印字符如下:
- 换行符:\n
- 制表符:\t
我们以匹配换行符为例,有如下代码:
1 2 3 4 5 6 7 8 9 |
import re a = "what's her name" b = '''what's her name''' c = '\n' x = re.search(c, a) y = re.search(c, b) print(x) print(y) |
结果为:
1 2 |
None <_sre.SRE_Match object; span=(10, 11), match='\n'> |
在字符串b
中包含了一个换行符\n
,而字符串a
中却没有。最后打印的x
返回None
,表示没有匹配结果。
通用字符:
接下来介绍通用字符,常见的通用字符如下:
- \w:字母、数字或下划线
- \W:除字母、数字和下划线
- \d:十进制数字
- \D:除十进制数字
- \s:空白字符
- \S:除空白字符
我们通过以下程序讲解通用字符的使用:
1 2 3 4 5 |
import re a = "what's 520 mean" b = '\w\d\s\w' x = re.search(b, a) print(x.group()) |
结果为:
1 |
20 m |
可以看出,我们要匹配4个原子,其中第一个和第四个原子可以是字母或数字或下划线;第二个原子必须是十进制数字;第三个原子必须是空白符。满足这种情况的四个字符只能是20 m
了。
原子表:
所谓原子表,就是将一系列原子放进[]
中表示一个集合。在匹配的时候,从原子表中依次选出一个原子进行匹配。如果在原子表里面的最前方加上^
符号,则表示匹配除了这些原子以外的元素,代码演示如下:
1 2 3 4 5 6 7 8 9 10 11 |
import re a = "mynameisfinthon" b = 'f[ijk]nthon' c = 'f[abc]nthon' d = 'f[^abc]nthon' x = re.search(b, a) y = re.search(c, a) z = re.search(d, a) print(x.group()) print(y) print(z.group()) |
结果为:
1 2 3 |
finthon None finthon |
可以看出,模式b
会依次从[ijk]
中选出原子组成匹配模式去匹配字符串a
;而模式c
里面的原子都匹配不出结果,返回None
;在模式d
中,只要不是[abc]
中的原子都行,最后匹配出结果'finthon'
。
元字符
元字符是指一些具有特殊意义的符号。常见的元字符有:
- .:除换行符以外的任意字符
- ^:开始位置
- $:结束位置
- *:0\1\多次
- ?:0\1次
- +:1\多次
- {n}:恰好n次
- {n,}:至少n次
- {n,m}:n到m次
- |:或
- ():模式单元
首先介绍.
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.r' c = 'n..e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y.group()) |
结果为:
1 2 |
her name |
.
代表了除换行符之外的任意字符,也就意味着在一行当中匹配字符。因此模式b
中'h.r'
只需要匹配一个字符;模式c
中'n..e'
只要两个字符。最后打印出结果'her'
和'name'
。
接下来介绍^
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = '^h.r' c = '^w..t' x = re.search(b, a) y = re.search(c, a) print(x) print(y.group()) |
结果为:
1 2 |
None what |
可以看出,此时^
字符的含义跟在原子表中的意思完全不同。在这里表示从头开始匹配,因此模式b
不符合要求,返回None
;只有模式c
可以匹配出结果'what'
。
接下来介绍$
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.r$' c = 'n..e$' x = re.search(b, a) y = re.search(c, a) print(x) print(y.group()) |
结果为:
1 2 |
None name |
可以看出,$
字符匹配的结尾部分,因此模式b
不符合要求,返回None
;而模式c
可以匹配出结果'name'
。
接下来介绍*
字符,输入如下程序:
1 2 3 4 5 |
import re a = "what's her name" b = 'h.*r' x = re.search(b, a) print(x.group()) |
结果为:
1 |
hat's her |
可以看出,.*
字符组合表示在一行中匹配任意多字符。需要注意的是正则匹配中默认使用贪婪模式,也就是尽可能多的匹配'h'
和'r'
之间的原子,所以得到结果"hat's her"
。至于贪婪模式和懒惰模式在后面也会讲到。
接下来介绍?
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.?r' c = 'n.?e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y) |
结果为:
1 2 |
her None |
可以看出,.?
组合表示在一行中,只能有0个或1个字符出现,模式b
中可以匹配差一个原子的结果'her'
,而模式c
中'n'
和'e'
之间只能有一个原子,匹配不出结果,返回None
。
接下来介绍+
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.+r' c = 'n.+e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y.group()) |
结果为:
1 2 |
hat's her name |
可以看出,.+
组合表示在一行中,只能有1个或多个字符出现,模式b
中按照贪婪原则可以匹配结果"hat's her"
,而模式c
此时也能匹配出结果'name'
。
接下来介绍{n}
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.{1}r' c = 'n.{3}e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y) |
结果为:
1 2 |
her None |
可以看出,{n}
能够指定前面原子出现n次数,因此模式b
只需要一个原子,可以匹配结果'her'
,而模式c
中'n'
和'e'
之间必须要有3个原子,没有匹配结果,返回None
。
接下来介绍{n,}
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.{1,}r' c = 'n.{3,}e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y) |
结果为:
1 2 |
hat's her None |
可以看出,{n,}
指定前面原子出现至少n次,因此模式b
得到匹配结果"hat's her"
,而模式c
匹配不到结果,返回None
。
接下来介绍{n,m}
字符,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.{1,3}r' c = 'n.{3,5}e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y) |
结果为:
1 2 |
her None |
可以看出,{n,m}
指定前面原子可以出现n到m次之间。因此模式b
得到匹配结果'her'
,而模式c
匹配不到结果,返回None
。
接下来介绍|
字符,输入如下程序:
1 2 3 4 5 |
import re a = "what's her name" b = 'h.{1,3}r|n.{3,5}e' x = re.search(b, a) print(x.group()) |
结果为:
1 |
her |
可以看出,|
字符能够判断前后两个表达式,只要其中有一个符合要求,就能匹配出结果。在这里虽然'n.{3,5}e'
没有匹配出结果,但是'h.{1,3}r'
能够匹配出结果'her'
。
接下来介绍()
字符,输入如下程序:
1 2 3 4 5 |
import re a = "what's her name" b = 'h(.)r' x = re.findall(b, a) print(x) |
结果为:
1 |
['e'] |
可以看出,()
的作用是想取什么数据,就把它用()
括起来。这里使用了findall()
函数,该函数将匹配结果以列表形式返回。
模式修正符
模式修正符能够对整个正则匹配模式进行修正,从而使匹配结果发生改变。常见的模式修正符有:
- I:忽略大小写
- M:多行匹配
- L:本地化识别匹配
- U:unicode
- S:匹配包括换行符
首先介绍I
模式修正符,输入如下程序:
1 2 3 4 5 |
import re a = "what's HER name" b = 'h.r' x = re.search(b, a, re.I) print(x.group()) |
结果为:
1 |
HER |
可以看出,使用模式修正符I
以后,模式b
可以进行忽略大小写的匹配。
接着介绍S
模式修正符,输入如下程序:
1 2 3 4 5 6 |
import re a = '''what's her name''' b = 'h.*e' x = re.search(b, a, re.S) print(x.group()) |
结果为:
1 2 |
hat's her name |
可以看出,使用模式修正符S
以后,模式b
可以进行多行匹配。
贪婪模式和懒惰模式
之前也介绍了在正则表达式中,会默认匹配尽量多的数据,也就是所谓的贪婪模式。而如果想改成懒惰模式,匹配的更加少,更加精确就要通过如下方式:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.*e' c = 'h.*?e' x = re.search(b, a) y = re.search(c, a) print(x.group()) print(y.group()) |
结果为:
1 2 |
hat's her name hat's he |
可以看出,模式b
是贪婪模式;模式c
是懒惰模式,通过加入一个?
字符变成尽量少的匹配结果。
正则表达式函数
在正则表达式中有许多函数,如下表所示:
函数 | 描述 |
---|---|
compile(pattern, flags=0) | 对正则表达式模式pattern进行编译,flags是可选标识符,返回一个regex对象 |
match(pattern, string, flags=0) | 从string开始使用pattern匹配,flags是可选标识符,如果匹配成功,返回一个匹配对象,否则返回None |
search(pattern, string, flags=0) | 使用pattern匹配string,flags是可选标识符,如果匹配成功,返回一个匹配对象,否则返回None |
findall(pattern, string, flags) | 使用pattern匹配string中的所有结果,返回一个匹配对象的列表 |
finditer(pattern, string, flags) | 和findall()相同,但返回的是一个迭代器,对于每个匹配,该迭代器返回一个匹配对象 |
split(pattern, string, max=0) | 根据pattern将string分割成一个列表,最多分割max次,默认全部分割 |
sub(pattern, repl, string, max=0) | 根据pattern将string中的元素用repl取代 |
group(num=0) | 返回全部匹配对象 |
groups() | 返回一个包含全部匹配的子组的元组,如果没成功匹配,则返回一个空元组 |
接下来会一一介绍。
首先是compile()
函数,输入如下程序:
1 2 3 4 5 |
import re a = "what's her name" b = 'h.*e' x = re.compile(b).search(a) #不考虑匹配速度的话,等价于re.search(b, a) print(x.group()) |
结果为:
1 |
hat's her name |
compile()
函数的作用就是先将匹配模式进行编译,编译后的模式能够更快的进行匹配。经过编译以后直接调用正则表达式方法(注意函数和方法的区别)。如果不去计较匹配的速度,它是等价于re.search(b, a)
。
接下来介绍match()
函数,输入如下程序:
1 2 3 4 5 6 7 8 |
import re a = "what's her name" b = 'h.*e' c = 'w.*t' x = re.match(b, a) y = re.match(c, a) print(x) print(y.group()) |
结果为:
1 2 |
None what |
match()
函数必须是从字符串a
的开头进行匹配。
接下来介绍findall()
函数,输入如下程序:
1 2 3 4 5 |
import re a = "what's her name here" b = 'h.*?e' x = re.findall(b, a) print(x) |
结果为:
1 |
["hat's he", 'he'] |
findall()
函数返回包含所有匹配结果的列表。
接下来介绍finditer()
函数,输入如下程序:
1 2 3 4 5 6 |
import re a = "what's her name here" b = 'h.*?e' x = re.finditer(b, a) print(next(x).group()) print(x.__next__().group()) |
结果为:
1 2 |
hat's he he |
finditer()
函数返回的是一个迭代器,因此就可以使用迭代器的next()
函数或__next__()
方法调用匹配对象。
接下来介绍split()
函数,输入如下程序:
1 2 3 4 5 |
import re a = "a:b:c" b = ':' x = re.split(b, a) print(x) |
结果为:
1 |
['a', 'b', 'c'] |
split()
函数返回的是一个经过字符分割后的列表。
接下来介绍sub()
函数,输入如下程序:
1 2 3 4 5 |
import re a = "a:b:c" b = ':' x = re.sub(b, '@', a) print(x) |
结果为:
1 |
a@b@c |
sub()
函数用于替换字符,结果也非常好理解。
总结
本文详细地介绍了正则表达式中各种字符和各种正则表达式函数的作用。当然正则表达式本身还有一些非常强大的功能,本文并没有涉及到。主要想通过一种简单的方式告诉大家,正则表达式使用起来其实不是很难。对于python新手来说,要多理解特殊字符的含义,记不住没关系可以再次翻阅。作为python进阶篇中的基础部分,学好正则表达式对以后深入研究其他方向很有帮助,比如爬虫中就需要经常使用正则表达式,获取想要的数据信息。PS:这是我目前写的最长的一篇文章,如果你耐心看完,我也很佩服你,哈哈!!!