你是不是也遇到过这些问题?

  •  想从一堆文本里挑出所有手机号,手动找太费眼;

  •  想把文档里的日期格式从“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

小白总结:先记这些,够用了!

不用背完所有符号,先记住这几个“高频组合”,就能解决大部分问题:

  1.  找数字:\d+(1个或多个数字)
  2.  找单词:\b\w+\b(完整单词)
  3.  找固定长度内容:\d{11}(11位手机号)
  4.  匹配标签/日期:用()分组,\1引用

小白练习:动手试试!

  1.  从文本里提取所有邮箱:r'[a-zA-Z0-9_]+@[a-zA-Z0-9]+\.[a-zA-Z]+'
  2.  把“2025-09-01”改成“2025年09月01日”:re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\1年\2月\3日', date)

正则的核心是“多练”——刚开始可以先抄例子,改改参数看结果变化,慢慢就会有感觉啦!

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐