1. 什么是正则表达式
正则表达式:也成为规则表达式,英文名称 Regular Expression,我们在程序中经常会缩写为 regex 或者 regexp,专门用于进行文本检索、匹配、替换等操作的一种技术。
注意:正则表达式是一种独立的技术,并不是某编程语言独有的
正则表达式,是一种特殊的符号,这样的符号是需要解释才能使用的,也就是需要正则表达式引擎来进行解释,目前正则表达式的引擎主要分三种:DFA,NFA、POSIX NFA,有兴趣了正则表达式引擎的童鞋,可以自己查看资料
2. 正则表达式语法结构
接下来,我们开始了解这样一个神秘的可以类似人类神经网络一样思考问题的技术的语法结构。
注意:我们通过 python 程序进行测试,但是正则表达式的语法结构在各种语言环境中都是通用的。
2.1. 入门案例:了解正则表达式
我们通过一个简单的案例入手:通常情况下,我们会验证用户输入的手机号码是否合法,是否 156/186/188 开头的手机号码,如果按照常规验证手段,就需要对字符串进行拆分处理,然后逐步匹配
重要提示:python 中提供了
re
模块,包含了正则表达式的所有功能,专门用于进行正则表达式的处理;
我们首先看一下,常规的手机号码验证过程
userphone = input("请输入手机号码:") |
执行上面的代码,分别输入不同的手机号码,结果如下
请输入手机号码:188
长度不合法请输入手机号码:15568686868
开头数字不合法请输入手机号码:1566868686a
不能包含非法字符请输入手机号码:15688888888
手机号码合法
我们再次使用正则表达式来改造这段程序
注意:如果下面的程序中出现了一些语法不是很明白,没关系,后面会详细讲解
import re |
执行上面的代码,我们得到正常验证的结果,大家可以自己试一试。
我们从这两套代码中,可以看出来,使用了正则表达式之后的程序变得非常简洁了,那保持好你的冲动和热情,让正则表达式来搞事吧
2.3. python 中的正则表达式模块 re
python 提供的正则表达式处理模块 re,提供了各种正则表达式的处理函数
2.3.1 字符串查询匹配的函数:
函数 | 描述 |
---|---|
re.match(reg, info) | 用于在开始位置匹配目标字符串 info 中符合正则表达式 reg 的字符,匹配成功会返回一个 match 对象,匹配不成功返回 None |
re.search(reg, info) | 扫描整个字符串 info,使用正则表达式 reg 进行匹配,匹配成功返回匹配的第一个 match 对象,匹配不成功返回 None |
re.findall(reg, info) | 扫描整个字符串 info,将符合正则表达式 reg 的字符全部提取出来存放在列表中返回 |
re.fullmatch(reg, info) | 扫描整个字符串,如果整个字符串都包含在正则表达式表示的范围中,返回整个字符串,否则返回 None |
re.finditer(reg, info) | 扫描整个字符串,将匹配到的字符保存在一个可以遍历的列表中 |
参考官方 re.py 源代码如下:
def match(pattern, string, flags=0): |
2.3.2 字符串拆分替换的函数:
函数 | 描述 |
---|---|
re.split(reg, string) | 使用指定的正则表达式 reg 匹配的字符,将字符串 string 拆分成一个字符串列表,如:re.split(r”\s+”, info),表示使用一个或者多个空白字符对字符串 info 进行拆分,并返回一个拆分后的字符串列表 |
re.sub(reg, repl, string) | 使用指定的字符串 repl 来替换目标字符串 string 中匹配正则表达式 reg 的字符 |
参考官方源代码如下:
def split(pattern, string, maxsplit=0, flags=0): |
接下来,我们进入正则表达式干货部分
2.4. 正则表达式中的元字符
在使用正则表达式的过程中,一些包含特殊含义的字符,用于表示字符串中一些特殊的位置,非常重要,我们先简单了解一下一些常用的元字符
元字符 | 描述 |
---|---|
^ | 表示匹配字符串的开头位置的字符 |
$ | 表示匹配字符串的结束位置的字符 |
. | 表示匹配任意一个字符 |
\d | 匹配一个数字字符 |
\D | 匹配一个非数字字符 |
\s | 匹配一个空白字符 |
\S | 匹配一个非空白字符 |
\w | 匹配一个数字 / 字母 / 下划线中任意一个字符 |
\W | 匹配一个非数字字母下划线的任意一个字符 |
\b | 匹配一个单词的边界 |
\B | 匹配不是单词的开头或者结束位置 |
上干货:代码案例
# 导入正则表达式模块 |
上述代码执行结果如下:
~ None
~<sre.SRE_Match object; span=(66, 75), match=’efficient’>
~[‘efficient’]
~->efficient
~<sre.SRE_Match object; span=(0, 5), match=’hello’>
~None
2.5. 正则表达式中的量词
正则表达式中的量词,是用于限定数量的特殊字符
量词 | 描述 |
---|---|
x* | 用于匹配符号 * 前面的字符出现 0 次或者多次 |
x+ | 用于匹配符号 + 前面的字符出现 1 次或者多次 |
x? | 用于匹配符号?前面的字符出现 0 次或者 1 次 |
x{n} | 用于匹配符号 {n} 前面的字符出现 n 次 |
x{m,n} | 用于匹配符号 {m,n} 前面的字符出现至少 m 次,最多 n 次 |
x{n,} | 用于匹配符号 {n,} 前面的字符出现至少 n 次 |
接上代码干货:
# 导入正则表达式模块 |
上述代码大家可以自行尝试并分析结果。执行结果如下:
2.6. 正则表达式中的范围匹配
在正则表达式中,针对字符的匹配,除了快捷的元字符的匹配,还有另一种使用方括号进行的范围匹配方式,具体如下:
范围 | 描述 |
---|---|
[0-9] | 用于匹配一个 0~9 之间的数字,等价于 \ d |
[^0-9] | 用于匹配一个非数字字符,等价于 \ D |
[3-6] | 用于匹配一个 3~6 之间的数字 |
[a-z] | 用于匹配一个 a~z 之间的字母 |
[A-Z] | 用于匹配一个 A~Z 之间的字母 |
[a-f] | 用于匹配一个 a~f 之间的字母 |
[a-zA-Z] | 用于匹配一个 a~z 或者 A-Z 之间的字母,匹配任意一个字母 |
[a-zA-Z0-9] | 用于匹配一个字母或者数字 |
[a-zA-Z0-9_] | 用于匹配一个字母或者数字或者下划线,等价于 \ w |
[^a-zA-Z0-9_] | 用于匹配一个非字母或者数字或者下划线,等价于 \ W |
注意:不要使用 [0-120] 来表示 0~120 之间的数字,这是错误的
整理测试代码如下:
# 引入正则表达式模块 |
上述代码执行结果如下:
[‘800’]
[‘ello’, ‘he’, ‘count’, ‘of’, ‘oday’, ‘is’]
[‘H’, ‘T’, ‘T’]
[‘Hello’, ‘The’, ‘count’, ‘of’, ‘Today’, ‘is’]
2.7. 正则表达式中的分组
正则表达式主要是用于进行字符串检索匹配操作的利器
在一次完整的匹配过程中,可以将匹配到的结果进行分组,这样就更加的细化了我们对匹配结果的操作
正则表达式通过圆括号 () 进行分组,以提取匹配结果的部分结果
常用的两种分组:
分组 | 描述 |
---|---|
(expression) | 使用圆括号直接分组;正则表达式本身匹配的结果就是一个组,可以通过 group() 或者 group(0) 获取;然后正则表达式中包含的圆括号就是按照顺序从 1 开始编号的小组 |
(?P | 使用圆括号分组,然后给当前的圆括号表示的小组命名为 name,可以通过 group(name) 进行数据的获取 |
废话少说,上干货:
# 引入正则表达式模块 |
上述代码就是从原始字符串中,通过正则表达式匹配得到一个结果,但是使用了分组之后,就可以将结果数据通过分组进行细化处理,执行结果如下:
请输入座机号码:021-6565789
<_sre.SRE_Match object; span=(0, 11), match=’021-6565789’>
021-6565789
021
6565789
<_sre.SRE_Match object; span=(0, 11), match=’021-6565789’>
021-6565789
021
6565789
2.8. 正则表达式中的特殊用法
使用分组的同时,会有一些特殊的使用方式如下:
表达式 | 描述 |
---|---|
(?:expression) | 作为正则表达式的一部分,但是匹配结果丢弃 |
(?=expression) | 匹配 expression 表达式前面的字符,如 “How are you doing” , 正则 “(? |
(?<=expression) | 匹配 expression 表达式后面的字符,如 “How are you doing” 正则 “(? |
(?!expression) | 匹配字符串后面不是 expression 表达式字符,如 “123abc” 正则 “\d{3}(?!\d)” 匹配 3 位数字后非数字的结果 |
(?<!expression) | 匹配字符串前面不是 expression 表达式字符,如 “abc123” 正则 “(?<![0-9])123”匹配”123”前面是非数字的结果也可写成”(?!<\d)123” |
2.9 正则表达式的贪婪模式和懒惰模式
在某些情况下,我们匹配的字符串出现一些特殊的规律时,就会出现匹配结果不尽如人意的意外情况
如:在下面的字符串中,将 div 标签中的所有内容获取出来
<div>内容1</div><p>这本来是不需要的内容</p><div>内容2</div> |
此时,我们想到的是,使用
regexp = r"<div>.*</div>" |
本意是使用上述代码来完成 div 开始标签和结束标签之间的内容匹配,但是,匹配的结果如下
<div> [内容1</div><p>这本来是不需要的内容</p><div>内容2] </div> |
我们可以看到,上面匹配的结果,是将字符串开头的
内容1</div><p>这本来是不需要的内容</p><div>内容2 |
上述就是我们要说的正则表达式的第一种模式:贪婪模式
贪婪模式:正则表达式匹配的一种模式,速度快,但是匹配的内容会从字符串两头向中间搜索匹配(比较贪婪~),一旦匹配选中,就不继续向字符串中间搜索了,过程如下:
开始:<div>内容1</div><p>这本来是不需要的内容</p><div>内容2</div> |
明显贪婪模式某些情况下,不是我们想要的,所以出现了另一种模式:懒惰模式
懒惰模式:正则表达式匹配的另一种模式,会首先搜索匹配正则表达式开始位置的字符,然后逐步向字符串的结束位置查找,一旦找到匹配的就返回,然后接着查找
regexp = r"<div>.*?</div>" |
开始:<div>内容1</div><p>这本来是不需要的内容</p><div>内容2</div> |
正则表达式匹配的两种模式:贪婪模式、懒惰模式
贪婪模式:从目标字符串的两头开始搜索,一次尽可能多的匹配符合条件的字符串,但是有可能会匹配到不需要的内容,正则表达式中的元字符、量词、范围等都模式是贪婪匹配模式,使用的时候一定要注意分析结果,如:<div>.*</div>
就是一个贪婪模式,用于匹配和之间所有的字符
懒惰模式:从目标字符串按照顺序从头到位进行检索匹配,尽可能的检索到最小范围的匹配结果,语法结构是在贪婪模式的表达式后面加上一个符号? 即可,如<div>.*?</div>
就是一个懒惰模式的正则,用于仅仅匹配最小范围的和之间的内容不论贪婪模式还是懒惰模式,都有适合自己使用的地方,大家一定要根据实际需求进行解决方案的确定