Python正则表达式入门:30分钟搞定文本查找替换!
你是不是也遇到过这些问题?
-
想从一堆文本里挑出所有手机号,手动找太费眼;
-
想把文档里的日期格式从“2025-11-01”改成“2025年12月01日”,一个个改太麻烦;
-
想判断用户输入的邮箱格式对不对,写一堆if-else太啰嗦。
其实,这些问题用正则表达式就能轻松解决!很多小白觉得正则“全是乱码符号,看不懂”,但其实它就像“文本的放大镜+手术刀”——只要掌握4个核心知识点,就能搞定80%的日常需求。今天就用最通俗的语言,带你入门Python正则表达式。
先搞懂:正则表达式到底是啥?(小白秒懂版)
正则表达式(简称“正则”),简单说就是“一套描述文本规律的符号”。比如:
-
想找“所有数字”,不用写“0-9每个数字都检查一遍”,直接用
\d就行; -
想找“11位手机号”,不用写“第一位是1,后面10位是数字”,直接用
1\d{10}就行。
在Python里,我们用自带的re库来用正则,而且推荐用“原始字符串”(比如r'\d'),不用写烦人的双重反斜杠(比如'\\d')——记住这个小技巧,后面写正则会轻松很多。
核心知识点1:字符类——找“什么样的字符”
字符类是正则的“基础零件”,用来定义“我要找的字符是什么类型”。比如找数字、找字母、找任意字符,都靠它。
先记最常用的几种,不用全背,用得多了自然就记住了:
| 符号 | 大白话解释 | 例子 |
. | 匹配任意单个字符(除了换行) | r'he.l' 能匹配“hell”“helm” |
\d | 匹配1个数字(0-9) | r'\d' 能匹配“1”“5”“9” |
\D | 匹配1个非数字 | r'\D' 能匹配“a”“#”“空格” |
\w | 匹配字母/数字/下划线 | r'\w' 能匹配“h”“3”“_” |
\s | 匹配空白字符(空格/制表符) | r'he\sllo' 能匹配“he llo” |
[abc] | 匹配“a”“b”“c”中的任意1个 | r'[abc]' 能匹配“a”“b”但不匹配“d” |
[a-z] | 匹配任意1个小写字母 | r'[a-z]' 能匹配“x”“y”但不匹配“A” |
[^abc] | 匹配“不是a、b、c”的任意字符 | r'[^abc]' 能匹配“d”“1”“!” |
小白实战1:提取文本里的字母和数字
比如有一段文本"hello 123 ! 456_abc",想把里面的“单词”(字母/数字/下划线组成的)都挑出来:
import re # 导入Python自带的正则库
text = "hello 123 ! 456_abc"
# 用re.findall找所有符合规则的内容,r'\w+'表示“1个或多个字母/数字/下划线”
result = re.findall(r'\w+', text)
print(result) # 输出:['hello', '123', '456_abc']
小白避坑:这些符号要“转义”
如果想匹配“.”“*”这些正则里的特殊符号,需要在前面加\(转义)。比如:
-
想匹配“www.baidu.com”里的“.”,不能直接写`r'www.baidu.com'`(会匹配“wwwxbaiducom”里的“x”),要写`r'www\.baidu\.com'`。
-
想匹配“-”本身(比如“123-456”里的“-”),如果放在
[]里,要写在开头或结尾,比如r'[-abc]'或r'[abc-]',不然会被当成“范围”(比如[a-z]是小写字母范围)。
核心知识点2:数量修饰符——找“多少个字符”
字符类定义了“找什么字符”,数量修饰符就定义“找多少个”。比如“1个或多个数字”“正好3个字母”,都靠它。
常用数量修饰符,记这几个就够了:
| 符号 | 大白话解释 | 例子 |
* | 0次或多次(可有可无,越多越好) | r'ab*' 能匹配“a”“ab”“abb” |
+ | 1次或多次(至少1个,越多越好) | r'ab+' 能匹配“ab”“abb”,不匹配“a” |
? | 0次或1次(可选,最多1个) | r'ab?' 能匹配“a”“ab”,不匹配“abb” |
{n} | 正好n次 | r'\d{3}' 能匹配“123”,不匹配“12” |
{n,} | 至少n次 | r'\d{2,}' 能匹配“12”“123”“1234” |
{n,m} | n到m次(包括n和m) | r'\d{2,4}' 能匹配“12”“123”“1234” |
小白实战1:提取2-4位数字
比如文本"12 345 6789 1",想挑出长度在2-4位之间的数字:
import re
text = "12 345 6789 1"
# r'\d{2,4}'表示“2到4个连续数字”
result = re.findall(r'\d{2,4}', text)
print(result) # 输出:['12', '345', '6789']
小白必懂:贪婪匹配 vs 非贪婪匹配
正则默认是“贪婪”的——能多匹配就多匹配。如果想让它“见好就收”,就在数量修饰符后面加个?。
举个例子,文本"acbcb",用两种方式匹配:
import re
text = "acbcb"
# 贪婪匹配:a.+b → 从a开始,找尽可能多的字符,直到最后一个b
print(re.findall(r'a.+b', text)) # 输出:['acbcb']
# 非贪婪匹配:a.+?b → 从a开始,找最少的字符,直到第一个b
print(re.findall(r'a.+?b', text)) # 输出:['acb']
简单记:想“少匹配”,就加?。
核心知识点3:边界与位置——找“在什么位置”
有时候,我们不仅要找“什么字符”,还要限定“字符在什么位置”。比如“找以ing结尾的单词”,而不是“单词里包含ing的部分”。
边界符不匹配具体字符,只匹配“位置”(比如开头、结尾、单词间隙):
| 符号 | 大白话解释 | 例子 |
^ | 字符串开头(或每行开头,多行模式) | r'^he' 能匹配“hello”,不匹配“xhello” |
$ | 字符串结尾(或每行结尾,多行模式) | r'lo$' 能匹配“hello”,不匹配“hellox” |
\b | 单词边界(单词和空格/标点的间隙) | r'\bcat\b' 能匹配“cat”,不匹配“category” |
\B | 非单词边界 | r'\Bcat\B' 能匹配“category”里的“cat”,不匹配“cat” |
小白实战:找以“ing”结尾的单词
比如文本"I am singing and running fast, something is wrong",想挑出“singing”“running”,而不是“something”里的“ing”:
import re
text = "I am singing and running fast, something is wrong"
# r'\b\w+ing\b':\b是单词边界,\w+ing是“字母+ing”
result = re.findall(r'\b\w+ing\b', text)
print(result) # 输出:['singing', 'running']
解释:“something”里的“ing”在单词中间(前面是“ometh”),没有\b(单词边界),所以不会被匹配到。
核心知识点4:分组与引用——让正则更灵活
有时候我们需要“重复利用”正则里的某部分,比如“匹配HTML标签(如粗体)”,要保证开头标签和结尾标签一致(不能是粗体),这时候就需要“分组”。
1. 普通分组:用()捕获,用\1引用
比如匹配<b>粗体</b>``<i>斜体</i>这类标签,保证开头和结尾标签相同:
import re
html = "<b>粗体</b><i>斜体</i><b>错误标签</i>"
# r'<(\w+)>(.*?)</\1>':
# 1. (\w+):捕获标签名(比如b、i),记为“分组1”
# 2. (.*?):捕获标签里的内容(非贪婪匹配)
# 3. </\1>:引用分组1的标签名,保证结尾标签和开头一致
result = re.findall(r'<(\w+)>(.*?)</\1>', html)
print(result) # 输出:[('b', '粗体'), ('i', '斜体')]
解释:<b>错误标签</i>里,开头是b,结尾是i,\1引用的是b,所以不会被匹配到。
2. 命名分组:用(?P<name>...),更直观
普通分组用\1“\2”记,容易搞混,命名分组可以给分组起个名字,比如“year”“month”“day”:
import re
date = "2025-09-01"
# r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})':
# 给每个分组起名字,分别对应年、月、日
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', date)
# 通过名字获取分组内容,比记\1更简单
print(m.group('year'), m.group('month'), m.group('day')) # 输出:2025 09 01
3. 分组替换:修改文本格式
比如把手机号“123-456-7890”中间4位换成星号“123-***-7890”:
import re
phone = "123-456-7890"
# r'(\d{3})-\d{3}-(\d{4})':捕获前3位和后4位,中间3位不捕获
# 替换为 r'\1-***-\2':\1是前3位,\2是后4位
masked_phone = re.sub(r'(\d{3})-\d{3}-(\d{4})', r'\1-***-\2', phone)
print(masked_phone) # 输出:123-***-7890
小白总结:先记这些,够用了!
不用背完所有符号,先记住这几个“高频组合”,就能解决大部分问题:
- 找数字:
\d+(1个或多个数字) - 找单词:
\b\w+\b(完整单词) - 找固定长度内容:
\d{11}(11位手机号) - 匹配标签/日期:用
()分组,\1引用
小白练习:动手试试!
- 从文本里提取所有邮箱:
r'[a-zA-Z0-9_]+@[a-zA-Z0-9]+\.[a-zA-Z]+' - 把“2025-09-01”改成“2025年09月01日”:
re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\1年\2月\3日', date)
正则的核心是“多练”——刚开始可以先抄例子,改改参数看结果变化,慢慢就会有感觉啦!
更多推荐
所有评论(0)