Python 正则表达式学习笔记(小白超详细版)(一)

正则表达式(Regular Expression,简称 regex/re)是用特殊字符组合匹配、查找、替换文本的工具 ,Python 内置 re 模块支持正则操作,无需额外安装。

这份笔记从基础概念→核心语法→re 模块常用方法→实战案例→避坑指南逐步讲解,全程配 Python 代码示例,小白也能跟着练。


一、前置准备:re 模块基础

Python 正则的核心是内置 re 模块,使用前需导入:

python

复制代码
import re  # 导入正则模块

常用核心函数(先记个大概,后面详细讲)

函数名 作用 返回值
re.match(pattern, str) 字符串开头匹配正则 匹配成功返回匹配对象,否则 None
re.search(pattern, str) 整个字符串找第一个匹配项 匹配成功返回匹配对象,否则 None
re.findall(pattern, str) 查找所有匹配项 列表(无匹配返回空列表)
re.sub(pattern, repl, str) 用 repl 替换字符串中所有匹配项 替换后的新字符串
re.split(pattern, str) 按正则规则分割字符串 分割后的列表

二、正则基础:字符匹配(最核心的基础)

正则的本质是用 "规则字符" 匹配 "文本字符",先从最基础的字符匹配学起。

1. 普通字符:直接匹配

普通字符(字母、数字、下划线)会精确匹配自身。

python

复制代码
# 匹配字符串开头的"python"
result = re.match("python", "python学习真有趣")
print(result)  # <re.Match object; span=(0, 6), match='python'>
print(result.group())  # 提取匹配结果:python
  • 若匹配失败,re.match 返回 None

    python

    复制代码
    result = re.match("java", "python学习真有趣")
    print(result)  # None

2. 特殊字符:需转义(. ^ $ * + ? \ [] {} ( ) | )

这些字符有特殊含义,若要匹配字符本身 ,需用反斜杠 \ 转义。

python

复制代码
# 匹配字符串中的"."(正则中.是特殊字符,需转义)
result = re.search(r"\.", "python.123")
print(result.group())  # .

提示:Python 中推荐用 ** 原始字符串(r"")** 写正则,避免反斜杠被 Python 解析为转义符(如 \n 换行)。

3. 通配符:匹配任意字符(除换行)

. 是通配符,匹配除换行符(\n)外的任意单个字符

python

复制代码
# 匹配"p...n"(p开头,中间3个任意字符,n结尾)
result = re.match("p...n", "python")
print(result.group())  # python

# 匹配失败(中间只有2个字符)
result = re.match("p...n", "pythn")
print(result)  # None

三、正则核心:重复匹配(控制字符出现次数)

正则中量词用于控制前一个字符 / 分组的出现次数,是正则的核心语法之一。

常用量词表

量词 含义 示例
* 匹配0 次或多次 a* → a、aa、aaa
+ 匹配1 次或多次(至少 1 次) a+ → a、aa、aaa
? 匹配0 次或 1 次(可选) a? → 空、a
{n} 匹配恰好 n 次 a{3} → aaa
{n,} 匹配至少 n 次 a{2,} → aa、aaa
{n,m} 匹配n 到 m 次(包含 n、m) a{2,4} → aa、aaa、aaaa

代码示例

python

复制代码
# 1. *:0次或多次
print(re.match("a*", "aaa").group())  # aaa
print(re.match("a*", "").group())     # 空字符串

# 2. +:至少1次
print(re.match("a+", "aaa").group())  # aaa
print(re.match("a+", ""))            # None

# 3. ?:0或1次
print(re.match("a?", "a").group())    # a
print(re.match("a?", "").group())     # 空

# 4. {n,m}:指定次数
print(re.match("a{2,4}", "aaa").group())  # aaa
print(re.match("a{2}", "aa").group())     # aa
print(re.match("a{2}", "a"))             # None

关键:量词作用范围

量词只作用于前一个字符 / 分组 ,加括号 () 可让量词作用于整个分组

python

复制代码
# 无括号:+只作用于b
print(re.match("ab+", "abbb").group())  # abbb
# 有括号:+作用于整个ab
print(re.match("(ab)+", "ababab").group())  # ababab

四、正则进阶:字符集与分组(精准匹配范围)

1. 字符集 []:匹配范围内的单个字符

用方括号 [] 定义字符范围 ,匹配其中任意一个字符(单个字符!)。

常用字符集写法
写法 含义 示例
[abc] 匹配 a、b、c 中的任意一个 [abc] → a、b、c
[a-z] 匹配小写字母 a-z [a-z] → a、b...z
[A-Z] 匹配大写字母 A-Z [A-Z] → A、B...Z
[0-9] 匹配数字 0-9(等价于 \d) [0-9] → 0、1...9
[^abc] 匹配除 a、b、c 外的任意字符(^ 取反) [^abc] → d、1、@
代码示例

python

复制代码
# 匹配开头是a、b、c中的一个,后面跟任意字符
print(re.match("[abc]123", "b123").group())  # b123
print(re.match("[a-z]python", "xpython").group())  # xpython

# 取反:匹配非数字
print(re.search("[^0-9]", "123a456").group())  # a

2. 分组 ():打包 + 提取 + 反向引用

分组是正则的核心功能,前面讲过,这里结合 Python 详细讲捕获分组非捕获分组

(1)捕获分组:提取匹配内容(最常用)

() 会将括号内的匹配结果捕获 ,可通过 group(n) 提取(n=1,2,3...),group()group(0) 提取整体匹配结果。

案例 1:提取手机号(3 位区号 + 8 位号码)

python

复制代码
# 正则:(\d{3})-(\d{8})  两个分组分别捕获区号和号码
text = "联系电话:010-12345678,021-87654321"
result = re.search(r"(\d{3})-(\d{8})", text)

# 提取整体结果
print("整体匹配:", result.group())  # 010-12345678
# 提取第1个分组(区号)
print("分组1(区号):", result.group(1))  # 010
# 提取第2个分组(号码)
print("分组2(号码):", result.group(2))  # 12345678
# 同时提取所有分组
print("所有分组:", result.groups())  # ('010', '12345678')
案例 2:提取日期(年 - 月 - 日)

python

复制代码
text = "今天是2026-04-10"
result = re.match(r"(\d{4})-(\d{2})-(\d{2})", text)
print("年:", result.group(1))  # 2026
print("月:", result.group(2))  # 04
print("日:", result.group(3))  # 10
(2)非捕获分组:仅分组不提取 (?:)

若只需用分组做整体控制 (如量词作用),不需要提取内容,用 (?:) 表示非捕获分组,不占用分组编号。

python

复制代码
# 匹配可选的http协议+域名
text = "https://www.baidu.com"
# 非捕获分组:(?:https?://)?  协议可选,但不提取
result = re.match(r"(?:https?://)?www\.\w+\.com", text)
print(result.group())  # https://www.baidu.com
# 无分组1,因为是非捕获
# print(result.group(1))  # 报错:IndexError: no such group
(3)反向引用:重复前面分组的内容 \1

\数字 引用前面分组的匹配结果,确保前后内容一致,常用于匹配成对标签、重复字符等。

python

复制代码
# 匹配成对的标签:<div>内容</div>
text = "<div>Python正则</div>"
# (\w+) 匹配标签名,\1 引用该标签名,保证前后一致
result = re.match(r"<(\w+)>.*?</\1>", text)
print(result.group())  # <div>Python正则</div>

# 匹配失败:标签不一致
text2 = "<div>Python正则</span>"
result2 = re.match(r"<(\w+)>.*?</\1>", text2)
print(result2)  # None

3. 预编译正则:提升效率 re.compile()

若同一个正则需要多次使用 ,用 re.compile() 预编译正则表达式,生成正则对象,再调用对象的方法,效率更高。

python

复制代码
# 预编译正则:匹配手机号
pattern = re.compile(r"(\d{3})-(\d{8})")

# 多次调用匹配
text1 = "010-12345678"
text2 = "021-87654321"

result1 = pattern.search(text1)
result2 = pattern.search(text2)

print(result1.group())  # 010-12345678
print(result2.group())  # 021-87654321

五、re 模块常用方法详细讲解(结合实战)

1. re.match():从开头匹配

  • 仅从字符串第一个字符开始匹配,失败则返回 None。
  • 常用场景:验证字符串是否以指定格式开头。

python

复制代码
# 验证是否以字母开头,后面跟3个数字
text = "A123test"
result = re.match(r"[A-Za-z]\d{3}", text)
print(result.group())  # A123

# 失败案例(开头不是字母)
text2 = "123Abc"
result2 = re.match(r"[A-Za-z]\d{3}", text2)
print(result2)  # None

2. re.search():全局查找第一个匹配

  • 扫描整个字符串,找到第一个匹配项即返回,无需从开头匹配。
  • 常用场景:查找文本中任意位置的匹配内容。

python

复制代码
# 查找文本中第一个数字串
text = "价格:99元,数量:5件"
result = re.search(r"\d+", text)  # \d+ 匹配1个或多个数字
print(result.group())  # 99

3. re.findall():查找所有匹配项(最常用)

  • 返回列表,包含所有匹配项;无匹配则返回空列表。
  • 若正则有分组,返回分组内容组成的元组列表
案例 1:提取文本中所有数字

python

复制代码
text = "Python3.11,发布于2022年,最新版本3.12"
result = re.findall(r"\d+\.?\d*", text)  # 匹配版本号(如3.11、3.12)和年份(2022)
print(result)  # ['3.11', '2022', '3.12']
案例 2:提取所有邮箱(分组提取用户名 + 域名)

python

复制代码
text = "邮箱:abc@qq.com,def@163.com,ghi@baidu.com"
# 分组:(\w+)@(\w+\.\w+)  提取用户名和域名
result = re.findall(r"(\w+)@(\w+\.\w+)", text)
print(result)  # [('abc', 'qq.com'), ('def', '163.com'), ('ghi', 'baidu.com')]
# 遍历提取结果
for user, domain in result:
    print(f"用户名:{user},域名:{domain}")

4. re.sub():替换匹配内容

  • 语法:re.sub(pattern, repl, string, count=0)
    • repl:替换的字符串(也可以是函数)
    • count:最大替换次数,0 表示替换所有。
案例 1:替换所有数字为 "#"

python

复制代码
text = "今天是2026年04月10日"
# 替换所有数字为#
result = re.sub(r"\d+", "#", text)
print(result)  # 今天是#年#月#日
案例 2:替换手机号中间 4 位为 ****(分组替换)

python

复制代码
text = "手机号:13812345678,13987654321"
# 正则:(\d{3})\d{4}(\d{4})  分组捕获前3位和后4位
# repl:\1****\2  引用分组1和分组2
result = re.sub(r"(\d{3})\d{4}(\d{4})", r"\1****\2", text)
print(result)  # 手机号:138****5678,139****4321

5. re.split():按正则分割字符串

  • 按正则规则分割字符串,返回列表。
案例 1:按逗号、分号、空格分割

python

复制代码
text = "苹果,香蕉;橙子 葡萄"
# 分割规则:[,; ]  匹配逗号、分号、空格
result = re.split(r"[,; ]", text)
print(result)  # ['苹果', '香蕉', '橙子', '葡萄']
案例 2:分割字符串并保留分割符

python

复制代码
text = "hello-world_python"
# 分割符为-或_,用()包裹则保留分割符
result = re.split(r"(-|_)", text)
print(result)  # ['hello', '-', 'world', '_', 'python']

六、实战案例:小白必练(覆盖日常 80% 场景)

实战 1:验证手机号(11 位数字,以 1 开头)

python

python 复制代码
def is_phone(phone):
    # 正则:^1[3-9]\d{9}$  ^开头,$结尾,1开头,3-9中间,9位数字
    pattern = re.compile(r"^1[3-9]\d{9}$")
    return pattern.match(phone) is not None

# 测试
print(is_phone("13812345678"))  # True
print(is_phone("1234567890"))   # False(不足11位)
print(is_phone("199abc12345"))  # False(含非数字)
print(is_phone("28812345678"))  # False(开头不是1)

实战 2:提取身份证中的出生日期(18 位身份证)

18 位身份证规则:前 6 位地址码 + 8 位出生日期(YYYYMMDD)+3 位顺序码 + 1 位校验码。

python

python 复制代码
def get_birthday(id_card):
    # 匹配18位身份证中的出生日期
    result = re.match(r"\d{6}(\d{8})\d{3}[\dX]", id_card)
    return result.group(1)
print(get_birthday('62052220090512173X'))
相关推荐
枫叶林FYL2 小时前
【Python高级工程与架构实战】项目五:生产级LLM Agent框架:基于PydanticAI的类型安全企业级实现
python·安全·架构
飞Link2 小时前
pprint 全量技术手册:复杂数据结构的结构化输出引擎
开发语言·前端·python
培风图南以星河揽胜2 小时前
幻想简历!博主本人期望的 AI Agent 全栈简历:Java + Python + Vue3 跨语言实战,代码已开源!
java·人工智能·python
第一程序员2 小时前
Python函数式编程:非科班转码者的入门指南
python·github
蓝色的杯子2 小时前
龙虾-OpenClaw一文详细了解-手搓OpenClaw-2 Provider层
人工智能·python
AI_Claude_code2 小时前
ZLibrary访问困境方案二:DNS-over-HTTPS/TLS配置与隐私保护实践
爬虫·python·网络协议·http·网络安全·https·网络爬虫
至此流年莫相忘2 小时前
数据库迁移工具——Alembic
python
Dxy12393102162 小时前
Python有哪些方法可以进行文本纠错
开发语言·python
却道天凉_好个秋2 小时前
pytorch(一):张量
人工智能·pytorch·python·深度学习