目录
[3. 贪婪和非贪婪模式](#3. 贪婪和非贪婪模式)
[5.sub()和 compile()方法](#5.sub()和 compile()方法)
简介
在 Python 的工具库中,正则表达式库(re)是处理文本的 "瑞士军刀",在字符串匹配、查找、替换等场景中有着不可替代的作用。无论是从海量文本中提取特定格式的信息,比如邮箱、手机号,还是对字符串进行复杂的校验与清洗,re 库都能高效完成对于数据分析师,它能快速从杂乱的数据中梳理出有效信息;对于开发人员,可用于表单验证、日志分析等多种任务;即便是普通学习者,掌握 re 库也能让文本处理效率大幅提升。这篇博客将带您探索 re 库的奥秘,从基础的匹配规则到进阶的模式修饰,逐步解锁它在文本处理中的强大能力,让您轻松应对各类字符串处理难题。
1.匹配字符串
匹配字符串表示从字符串中筛选出满足条件的信息,这里的条件要使用一种特殊的表达式,即正则表达式表示。
1.match()
函数
match()
函数的使用形式如下:
match(参数1,参数2)
功能:表示从参数 2(字符串类型数据)中查找满足参数 1(正则表达式)的内容,如果参数 2 起始位置匹配不成功的话,就返回None
;如果起始位置匹配成功,就返回匹配的内容。
python
import re
message = '张三、李四、王五、赵六'
result = re.match('张三',message)
print(result)
# 运行结果:<re.Match object; span=(0, 2), match='张三'>
返回的结果以正则的类型输出,其中span=(0,2)
指明匹配的位置,表示在字符串索引号为 0~2 的位置匹配成功,匹配的内容为张三,而下面的代码执行代码后将返回None
,虽然字符'三'
在message
,但并不位于message
的开头,所以匹配不成功。
python
import re
message = '张三、李四、王五、赵六'
result = re.match('三',message)#起始位置匹配不成功的话,就返回none
print(result)
# 运行结果为:None
2.search()
函数
search()
函数的使用形式如下:
python
search(参数1,参数2)
功能:表示从参数 2(字符串类型数据)中查找满足参数 1(正则表达式)的内容,如果匹配了多个参数 1,只返回第 1 个匹配成功的信息。
python
import re
message = '张三、李四、王五、赵六、王五'
result = re.search('王五',message)#从参数2中查找满足参数1的内容
print(result)#匹配了多个参数1,则只返回第1个匹配成功的信息
# 运行结果:<re.Match object; span=(6, 8), match='王五'>
第 3 行代码使用search()
函数从message
中匹配字符串'王五'
,由于message
中存在两个'王五'
,因此执行代码后会输出第 1 个'王五'
所在的位置及内容。
3.findall()
函数
findall()
函数的使用形式如下:
python
findall(参数1,参数2)
功能:表示从参数 2(字符串类型数据)中查找满足参数 1(正则表达式)的内容,如果匹配了多个参数 1,则返回匹配成功的全部信息。
python
import re
message = '张三、李四、王五、赵六、王五'
result = re.findall('王五',message)#从参数2中查找满足参数1的所有内容
print(result)
# ['王五', '王五']
findall()
并不返回匹配的位置,只返回匹配的全部内容。
2.正则表达式
1.表示字符范围
(1.)[xyz]
:字符集合,即匹配所包含的任意一个字符。例如[abc]
可以匹配plain
中的a
。
(2.)[a-z]
:字符范围,即匹配指定范围内的任意字符。例如[a-z]
可以匹配a
到z
范围内的任意小写字母。
python
import re
message = 'Python93,C87,Java63,C++88'
result_1 = re.search('[cn]',message)
result_2 = re.findall('[0-9]',message)
result_3 = re.findall('[cn][0-9]',message)
print(result_1,result_2,result_3)
# 运行结果:<re.Match object; span=(5, 6), match='n'> ['9', '3', '8', '7', '6', '3', '8', '8'] ['n9']
第 3 行代码表示从message
中匹配字符c
或n
,只要message
中包含c
或n
,执行代码后就会输出匹配到的第 1 个字符。需要注意的是大写的C
是不能匹配的。
第 4 行代码表示从message
中匹配0~9
的任何一个数字,即匹配全部数字,由于使用findall()
函数,因此程序会将message
中的全部数字输出。
第 5 行代码中的正则表达式包含[cn][0-9]
,表示需要匹配两个字符,且第 1 个字符是c
或n
、第 2 个字符是数字。
2.表示字符出现次数
*
:匹配前面的子表达式任意次(大于等于 0 次)。例如zo*
能匹配z
、zo
和zoo
,*
等价于{0,}
。+
:匹配前面的子表达式一次或多次。例如zo+
能匹配zo
和zoo
,但不能匹配z
,+
等价于{1,}
。?
:匹配前面的子表达式 0 次或一次。例如do(es)?
可以匹配do
或does
,?
等价于{0,1}
。- ^:匹配输入行首。
$
:匹配输入行尾。{n}
:匹配n
次,n
为非负整数。例如o{2}
不能匹配Bob
中的o
,但是能匹配food
中的两个o
。{n,}
:至少匹配n
次,n
为非负整数。例如o{2,}
不能匹配Bob
中的o
,但能匹配foooood
中的所有o
。o{1,}
等价于o+
,o{0,}
则等价于o*
。{n,m}
:最少匹配n
次且最多匹配m
次,m
和n
均为非负整数且n≤m
。例如o{1,3}
将匹配foooood
中的前 3 个o
和后 3 个o
。o{0,1}
等价于o?
。请注意,逗号和两个数之间不能有空格。
python
import re
message = 'da2a7ddbre77yifed777t3fefd7777b'
result = re.findall('[a-z]*[0-9][a-z]',message)
print(result)
# 运行结果:['da2a', '7d', '7y', '7t', '3f', '7b']
第 3 行代码的正则表达式中*
的前面为[a-z]
,表示匹配a~z
范围内的任意字符,[a-z]*
表示*
前面的表达式范围内的字符可以出现任意多次,[a-z]*[0-9][a-z]
表示匹配的最后一个字符是a~z
的字母,倒数第 2 个字符是一个数字。
案例1:(验证手机号码的正确性)
python
'''验证手机号码的正确性'''
import re
phone_num = input("请输入您的手机号码:")#13155558888
result = re.findall('^1[0-9]{10}$',phone_num)
print(result)
手机号码以 1 开头且一共有 11 位数字。在第 3 行代码中,正则表达式 ^1 [0 - 9]{10}中的表示开头必须是当使用时,可以不需要。表示匹配个字符,且必须是数字。表示在行尾,表示匹配了前面的 11 个数字后就必须结束。当用户输入的内容超过 11 位或不足 11 位时,与正则表达式不匹配,程序将返回 []。如果符合正则表达式就会输出号码。
案例2:(验证QQ号码是否合规)
python
import re
QQ_number = input("请输入您的QQ号:")
result = re.match('[1-9][0-9]{4,10}$',QQ_number)
print(result)
QQ 号码的位数为 5 - 11 位,且第 1 个数字不能为 0。第 3 行代码使用了 match (),需要从输入的内容首位开始匹配,因此匹配的正则表达式可以省略 ^,[1 - 9] 表示匹配的第 1 个数字只能为 1 - 9,不能为 0。[0 - 9]{4,10} 可以匹配 4 - 10 个数字,$ 表示必须要匹配到最后一个字符。如果输入的内容与正则表达式匹配,就会输出匹配内容;如果匹配失败则会输出 None。
案例3:(验证网站用户名的正确性)
python
import re
use_name = input("请输入您的用户名:")
result = re.findall('^[A-Za-z_][A-Za-z0-9_]{7,}$',use_name)
print(result)
网站用户名由字母(大小写均可)、数字和下划线组成,数字不能用作开头,且用户名长度要大于 8。第 3 行代码中正则表达式 ^[A - Za - z] 表示输入的内容首字母必须是 A - Z、a - z 或_,[A - Za - z0 - 9_]{7,} 表示至少匹配 7 个 A - Z、a - z、0 - 9 或_的字符。$ 表示一直匹配到结尾,结尾的字符也要满足范围是 A - Z、a - z、0 - 9 或_。
3.表示同一类字符
\d: 匹配一个数字字符,等价于 [0 - 9]。
\D: 匹配一个非数字字符,等价于 [^0 - 9]。
\s: 匹配任何可见字符,包括空格、制表符、分号等,等价于 [ \f\n\r\t\v]。
\S: 匹配任何不可见字符,等价于 [^ \f\n\r\t\v]。^ 在方括号中表示非,即不匹配输入字符的首位字符。
\w: 匹配包括下划线的任何单词字符,等价于 "[A - Za - z0 - 9_]"。
\W: 匹配任何不包括下划线的非单词字符,等价于 "[^A - Za - z0 - 9_]"。
\b: 匹配一个单词的边界,即单词中与空格邻接的字符。
\B:匹配非单词边界。例如 "er\B" 能匹配 "verb" 中的 "er",但不能匹配 "never" 中的 "er",因为 "er" 是 "never" 的单词边界。
\f:匹配一个分页符。
\n:匹配一个换行符。
\r:匹配一个回车符。
\t:匹配一个制表符。
\v:匹配一个垂直制表符。
案例4:(验证用户名的正确性)
python
import re
use_name = input("请输入您的用户名:")
result = re.findall('^[A-Za-z_]\w{7,}$',use_name)
print(result)
第 3 行代码中使用正则表达式 \w 替换了上一示例代码中的 [A - Za - z0 - 9_],效果与上一示例代码的效果相同。
案例5:(匹配非单词边界)
python
import re
message = 'verb very never every'
result = re.findall(r'\w+er\B',message) #\b是转义字符
print(result)
#['ver', 'ver', 'ever']
在第 3 行代码中,正则表达式 r'\w+er\B' 中的第一个 r 表示取消转义字符,因此不需要使用 \ 的形式。\w+er\B 中 \w + 表示从 message 中匹配出开头部分有一个或多个单词的部分,er\B 表示在 message 中匹配出 er 不是单词的边界的部分。由于 verb 中的 er 并不是单词的边界,且 er 的前面有一个单词 v,因此此字符串将会被匹配上。very 中的 ver 也能被匹配上。由于 never 中的 er 是单词的边界,因此不会被匹配上。every 中的 er 不是单词的边界,且 er 的前面有单词 ev,因此匹配的内容是 ever。
3. 贪婪和非贪婪模式
贪婪模式:默认匹配模式都是贪婪模式,匹配形式是尝试匹配尽可能多的字符,只要满足正则表达式,就会匹配最多的字符。
python
import re
message = 'ccc739134792hd'
result = re.findall('ccc\d+',message)
print(result)
# 运行结果:['ccc739134792']
第 3 行代码表示从 message 中匹配前 3 个字符为'ccc'的字符,其中 \d 表示匹配一个数字字符,+ 表示前面的子表达式一次或多次,即表示匹配一次或多次数字。由于默认匹配模式是贪婪模式,因此会尽可能多匹配数字。当从'ccc739134792hd'中匹配到了'ccc7'之后还会继续匹配尽可能多的数字。
非贪婪模式:匹配形式是尝试匹配尽可能少的字符,一旦满足正则表达式要求就不再继续匹配。在正则表达式操作符后面加上 "?" 可以将匹配模式转换为非贪婪模式。
python
import re
message = 'ccc739134792hd'
result = re.findall('ccc\d+?',message)
print(result)
# 运行结果:['ccc7']
该代码与上一个示例代码的区别在于第 3 行的正则表达式部分最后增加了一个 "?",表示使用非贪婪模式匹配,只要匹配到'ccc'且后面有一个数字就不会再继续往后匹配。
4.或与组
如果需要筛选出组合条件下的字符数据,可以使用或。
或:用 "|" 表示,表示将两个匹配条件进行组合。
案例6:(匹配表达式中两个匹配条件中的一个匹配条件)
python
import re
message = 'verb very never every'
result = re.findall('\w+ev|\w+ry',message)
print(result)
# 运行结果为:['very', 'nev', 'every']
第 3 行代码使用 | 表示从 message 中匹配满足'\w+ev'条件或者满足'\w+ry'条件的内容。'\w+ev'表示匹配一个或多个单词字符,且后面的字符为 ev。'\w+ry'表示匹配一个或多个单词字符,且后面的字符为 ry。
组:用 "(表达式)" 表示,表示将 () 中的表达式定义为组,并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存 9 个组)。
python
import re
message = 'verb very never every'
result = re.findall("e(.+)(.)r",message)
print(result)
# 运行结果为:[('rb very never ev', 'e')]
第 3 行代码使用了组的形式,表示从'verb very never every'中匹配'e (.+)(r)'条件的内容。因此匹配的内容第 1 个字符是 e,第 1 个组匹配一个或多个除'\n'和'\r'之外的任何单个字符(包含空格),默认使用贪婪模式进行匹配,该模式会尽可能多地匹配内容。第 2 个组匹配除'\n'和'\r'之外的任何单个字符(包含空格)。从字符串的第 1 个字符 e 开始,可以一直匹配到最后一个字符 r,因此匹配的内容是'verb very never ever'。但输出时只会输出组中的内容,并不会将所有匹配的内容输出,由于有两个组,因此会输出这两个组中的内容。
5.sub()和 compile()方法
sub () 和 compile () 方法分别用于将字符进行替换和将字符串转换为正则表达式。sub () 方法的使用形式如下,即将字符串参数 3 中所有与参数 1 匹配的字符替换为参数 2。
python
re.sub (参数 1, 参数 2, 参数 3)
python
import re
content ='dh932hf9f934hfnf39d'
content = re.sub('\d','0',content)
print(content)
# 运行结果为:dh000hf0f000hfnf00d
第 3 行代码将字符串 content 中满足正则表达式 \d 的内容全部替换为 0。
compile () 方法用于创建一个正则表达式对象,可以直接使用正则表达式对象的方法匹配其他内容。
python
import re
contentl ='2020 12 15 12:00'
pattern = re.compile('\d{2}:\d{2}')
print(pattern.findall(contentl))
# 运行结果为:['12:00']
第 3 行代码通过 compile () 方法创建了一个正则表达式对象 pattern,正则表达式的内容为'\d {2}:\d {2}',接下来可以直接使用 pattern 对象匹配其他内容。第 4 行代码直接使用 pattern. 方法的形式,匹配 content1 中满足 pattern 正则表达式对象的信息。
总结
正则表达式的知识点比较多,新手开始的时候并不建议背,花点时间去熟记就行,多琢磨琢磨里面的细节,下次看到的时候就明白是怎么回事就可以了。