Python中的正则表达式
一:正则表达式使用
1.什么是正则表达式?
正则表达式其实就是一套固定的规则,这套规则限制着字符串或者说是在过滤字符串
比如:
- 规则 \d{11}:匹配11位数字,用来验证手机号
- 规则 \w+@\w+.\w+:匹配邮箱格式
核心用途:
- 验证:判断字符串是否符合规则(比如手机号、邮箱、身份证号格式是否正确);
- 查找:从文本中找出所有符合规则的字符串(比如从新闻中提取所有日期);
- 提取:从匹配结果中提取关键信息(比如从邮箱中提取用户名);
- 替换:替换文本中符合规则的内容(比如过滤文本中的特殊字符)。
使用正则表达式之前需要进行导包:import re
使用正则表达式的核心流程:定义规则->执行匹配->提取结果
import re #导入正则表达式模块 res = re.match(r"h\w+","hello world") if res: print(res.group()) else: print("匹配失败!")re.match()只会根据字符的开头进行匹配,如果开头错误会直接失败,失败会返回None。
2.匹配单个字符的规则符号
| 符号 / 写法 | 匹配规则 | 补充说明 |
|---|---|---|
. | 匹配除换行符\n外的任意单个字符 | 开启re.DOTALL标志后,可匹配包含换行符在内的所有字符 |
[abc] | 匹配方括号内列举的任意一个字符 | 集合内为 “或” 逻辑;绝大多数元字符(如.、*)在集合内会失去特殊含义,仅作为普通字符匹配 |
[a-z] | 匹配指定字符范围内的任意一个字符 | 用-连接起止字符,支持[0-9]、[A-Za-z]等组合;-放在集合首尾时仅作为普通横杠字符匹配 |
[^abc] | 匹配不在方括号内的任意一个字符 | ^必须放在集合开头才表示取反;放在集合中间时仅作为普通字符匹配 |
\d | 匹配任意单个数字字符 | ASCII 模式等价于[0-9];Python 3 默认 Unicode 模式还会匹配全角数字等,开启re.ASCII后仅匹配半角数字 |
\D | 匹配任意单个非数字字符 | \d的取反形式,ASCII 模式等价于[^0-9] |
\w | 匹配任意单个单词字符 | ASCII 模式等价于[A-Za-z0-9_];Unicode 模式下还会匹配中文等语言文字 |
\W | 匹配任意单个非单词字符 | \w的取反形式,ASCII 模式等价于[^A-Za-z0-9_] |
\s | 匹配任意单个空白字符 | 包含空格、制表符\t、换行符\n、回车符\r、换页符\f、垂直制表符\v |
\S | 匹配任意单个非空白字符 | \s的取反形式 |
\ | 转义符,将正则元字符转义为普通字面字符 | 用于匹配符号本身,例如\.匹配字面点号、\[匹配左中括号、\\匹配反斜杠本身 |
3.匹多个字符的规则符号
| 符号 / 写法 | 匹配规则 | 补充说明 |
|---|---|---|
* | 匹配前面的字符 / 分组 0 次或多次,贪婪模式 | 尽可能多地匹配目标内容;等价写法为{0,} |
+ | 匹配前面的字符 / 分组 1 次或多次,贪婪模式 | 被匹配内容至少出现 1 次;等价写法为{1,} |
? | 匹配前面的字符 / 分组 0 次或 1 次,贪婪模式 | 用于标记可选内容;等价写法为{0,1} |
{n} | 匹配前面的字符 / 分组恰好 n 次 | n 为非负整数,例如\d{4}可匹配连续 4 位数字 |
{n,} | 匹配前面的字符 / 分组至少 n 次,贪婪模式 | 匹配次数无上限,优先匹配尽可能多的内容 |
{n,m} | 匹配前面的字符 / 分组 n 到 m 次,贪婪模式 | 包含 n 次和 m 次边界,优先匹配更多次数 |
量词+? | 将贪婪量词转为非贪婪(惰性)模式,尽可能少匹配 | 常见写法:*?、+?、??、{n,m}?;正则默认贪婪匹配,追加?后转为最少匹配 |
( ) | 分组,将括号内的多字符视为一个整体,可配合量词实现重复匹配 | 例如(ab)+可匹配ab、abab等;同时会捕获分组内的匹配结果 |
OK!那现在就可以施展他们两的组合技了!!!
\d* 匹配任意数量的数字字符,\d+ 匹配任意数量的数字字符。
# \d* 匹配任意数量的数字字符 res1 = re.match("\d*","123abc") print(res1.group()) # \d+ 匹配任意数量的数字字符 res2 = re.match("\d+","123abc") print(res2.group())他两的区别就是d*的结果可以为0以上的结果,d+只能为1以上的结果
\d? 最多匹配一个数字字符
# \d? 最多匹配一个数字字符 res = re.match("\d?","123abc") print(res.group())手机格式验证:
phone1 = "13212345678" phone2 = "1321234567" phone3 = "132abcdefgh" res1 = re.match("\d{11}",phone1) print(res1.group()) # 输出13212345678 res2 = re.match("\d{11}",phone2) print(res2.group()) # 报错:AttributeError res3 = re.match("\d{11}",phone3) print(res3.group()) # 报错:AttributeError4.核心规则:匹配开头和结尾
用^和$限定字符串的开头和结尾,确保整个字符串完全符合规则(避免部分匹配)。其实核心的意思就是打假,^打假开头防止尾部符合开头不符合规则;$符号打假尾部防止开头符合尾部不符合。
邮箱格式验证:
# 邮箱规则:数字/字母/下划线开头,@后面是数字字母,.后面是2-4位字母 #这里的\.表示只匹配.;如果写.表示匹配除换行符以外的所有字符 pattern = r"^\w+@\w+\.\w{2,4}$" # 要验证的邮箱 email1 = "abc123@qq.com" # 结果:abc123@qq.com email2 = "abc@123.xyz" # 结果:abc@123.xyz email3 = "abc123.com" # 结果:格式错误 email4 = "abc123@qq.com.cn" # 结果:格式错误 for email in [email1,email2,email3,email4]: res = re.match(pattern,email) if res: print(res.group()) else: print("格式错误")5.核心规则:匹配分组(进阶)
用()将部分规则分组,可实现“多选一”或“引用分组结果”,适合复杂匹配场景。
5.1多选一(|)
规则:(规则1|规则2|规则3),匹配任意一个符合的规则,只要符合其中一条就能输出,否则报错。
pattern = r"(red|blue|black)" string1 = "blue" string2 = "green" res1 = re.match(pattern,string1) res2 = re.match(pattern,string2) print(res1.group()) #输出:blue print(res2.group()) #不匹配,程序报错5.2引用分组结果(\num)
用\num引用前面第num个分组匹配的内容(比如匹配成对的HTML标签),适合成双成对出现的字符。(张三)在前面出现过,后面就使用 \数字 来代替,这里注意数字取决于第几个分组,而分组使用()来表示分组。
#<html>login</html> pattern = r"<(\w+)>\w+</\1>" string1 = "<html>login</html>" string2 = "<html>login</body>" res1 = re.match(pattern,string1) res2 = re.match(pattern,string2) print(res1.group()) #输出:<html>login</html> print(res2.group()) #不匹配,程序报错二:常用函数
回顾:re.match()
功能:从字符串开头开始匹配,开头不符合就返回None。
res1 = re.match("th","this is a good day") print(res1.group()) #输出:th res2 = re.match("th","a good day") print(res2.group()) #报错2.1核心函数:re.search()——全局找第一个匹配
功能·:search()会遍历整个字符串,不管开头是否匹配,只要找到第一个符合规则的就返回结果,如果没有就返回None,相当于文档里面的查找功能。
res1 = re.search("good","this is a good day") print(res1.group()) #输出:good res2 = re.search("is","this is a good day") print(res2.group()) #输出:is(出现多次只会返回第一个) res3 = re.search("cc","this is a good day") print(res3) #输出:None2.2核心函数:re.findall()——找所有匹配项(返回列表)
功能:findall()会遍历整个字符串,找到所有符合规则的非重叠子串,并将所有获取到的结果以列表的形式返回,如果没有找到就返回空列表。
res1 = re.findall(r"\d","abcd1234@#$") print(res1) #输出:['1', '2', '3', '4'] res2 = re.findall(r"\w{3}","good123ABC_xyz") print(res2) #输出:['goo', 'd12', '3AB', 'C_x'] res3 = re.findall(r"\d","abcd@#$") print(res3) #输出:[]2.3核心函数:re.sub()——替换匹配项
功能:sub()会遍历字符串,将所有匹配到规则的子串替换成指定内容;可以指定替换次数,默认情况下替换所有匹配项。
res1 = re.sub("is","am","hello,everyone! this is a good day!") print(res1) #输出:hello,everyone! tham am a good day! res2 = re.sub("is","am","hello,everyone! this is a good day!",count=1) print(res2) #输出:hello,everyone! tham is a good day!(只替换第一个) res3 = re.sub(r"\d","*","今天是第23天,温度是36°,湿度是48%") print(res3) #输出:今天是第**天,温度是**°,湿度是**% res4 = re.sub(r"\W","","sajs%&^$sa") print(res4) #输出:sajssa(删除所有特殊符号)三:贪婪匹配&非贪婪匹配
3.1什么是贪婪匹配/非贪婪匹配?
正则:
贪婪模式(默认):量词(如*、+、{m,n})会尽可能的匹配多的字符,一直到无法匹配为止。
非贪婪模式:在量词后加?,量词会尽可能匹配少的字符,满足规则即可。
3.2关键前提:只影响量词的匹配行为
受影响的量词:
不受影响的规则:.、\d、[a-z]等单个字符匹配规则。
3.3贪婪模式:
正则默认是贪婪模式,不用额外加任何符号,量词会自动匹配尽可能多的字符。这里可以理解为规则范围内取最大值作为标准。
res1 = re.match(r"\d*","1234abcd") print(res1.group()) #输出:1234 res2 = re.match(r"\d+","1234abcd") print(res2.group()) #输出:1234 res3 = re.match(r"\d{2,3}","1234abcd") print(res3.group()) #输出:1233.4非贪婪模式:
量词后面加?,能少拿就少拿。这里可以理解为规则范围内取最小值作为标准。
res1 = re.match(r"\d*?","1234abcd") print(res1.group()) #输出:""(空字符串) res2 = re.match(r"\d+?","1234abcd") print(res2.group()) #输出:1 res3 = re.match(r"\d{2,3}?","1234abcd") print(res3.group()) #输出:12重点实例:模糊匹配中的差异(注意?需要加在量词后面)
res_greedy = re.match(r"a.*b",string) res_non_greedy = re.match(r"a.*?b",string) print(res_greedy.group()) #输出:a123b123b print(res_non_greedy.group()) #输出:a123b3.5什么时候适合使用非贪婪模式?
常见场景:
1.提取:“最近匹配”的内容(如上面的a.*?b,提取a到最近的b)
2.避免匹配范围“过度扩大”(如提取HTML标签时,避免把多个标签当成一个)
3.明确需要“最少匹配”的场景(如匹配2-5个数字,只需要前2个)
示例:提取HTML标签中的内容(非贪婪必用)
string = "<div>第一部分</div><div>第二部分</div>" res_greedy = re.match(r"<div>.*</div>",string) res_non_greedy = re.match(r"<div>.*?</div>",string) print(res_greedy.group()) #输出:<div>第一部分</div><div>第二部分</div> print(res_non_greedy.group()) #输出:<div>第一部分</div>