Python3 re 正则表达式模块及应用

🎉摘要:本文详解 Python 正则表达式及 re 模块用法,涵盖基础语法、预定义字符、数量词与边界匹配。深入讲解 match、search、findall 等核心方法,提供手机号、邮箱验证及用户信息提取脱敏实战案例,助力开发者高效完成文本匹配、查找、替换与数据清洗任务。

在日常开发中,正则表达式常用来匹配、查找、替换字符串,是处理文本的神器。而且,几乎所有编程语言(Python/Java/JS/PHP 等)都支持正则表达式。

正则表达式常被用来:

  • 校验格式:手机号、邮箱、身份证、密码强度。

  • 提取内容:从文本里抓出网址、数字、关键词。

  • 替换文本:批量替换敏感词、格式化文本。

  • 分割字符串:按规则拆分文本。

正则表达式基础

正则表达式(Regular Expression,简称 Regex / RegExp)是一套用来描述字符串匹配规则的特殊语法公式,专门用来检索、匹配、提取、替换、校验文本。你可以把它理解为高级的模糊搜索模板。例如:

  • 匹配纯数字:^\d+$

  • 匹配11 位手机号:^1\d{10}$

  • 匹配邮箱:^\w+@\w+\.\w+$

上述示例中,^、$、\d、{}、\w 等等符号均属于正则表达式中的语法公式,均拥有特殊的意义。

正则表达式语法

普通字符

如果你要精确匹配某个字符串,可以直接写字符串。例如:

abc → 匹配字符串里的 abc

预定义字符

正则表达式预定义了一些字符,用来快速匹配某类字符串,如数字、任意字符等,如下:

  • .(点)  匹配任意一个字符(除换行)

  • \d 匹配一个数字(0-9)

  • \w 匹配字母 / 数字 / 下划线

  • \s 匹配空格 / 制表符

  • \D 匹配非数字

  • \W 匹配非字母数字下划线

数量词

正则表达式的数量词用来控制匹配次数,如 0 次或多次,1 次或多次,如下:

  • *      匹配 0 次或多次

  • +     匹配 1 次或多次

  • ?      匹配 0 次或 1 次

  • {n}    精确匹配 n 次

  • {n,}    至少匹配 n 次

  • {n,m}    匹配 n~m 次

边界与范围

正则表达式中除了预定义的基础字符和数量词外,还提供了边界和范围匹配语法,例如:

  • ^    字符串开头

  • $     字符串结尾

  • [abc]     匹配 a、b、c 中任意一个

  • [^abc]     匹配 不是 a/b/c 的字符

  • `     `或(如 `a b`)

  • ()     分组,提取内容用

简单示例

匹配大陆手机号

手机号格式校验,手机号是严格的 11 位,正则如下:

^1[3-9]\d{9}$

说明:

  • ^:匹配字符串开始位置,防止前面多带字符。

  • 1:手机号固定以1开头。

  • [3-9]:第二位只能是 3~9,排除 1、2 开头的无效号段。

  • \d{9}:后面严格跟着9 个数字。

  • $:匹配字符串结束位置,防止后面多带多余字符。

邮箱地址验证

验证电子邮箱地址,正则如下:

^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z]{2,}$

说明:

  • [a-zA-Z0-9_-]+:邮箱用户名,允许大小写字母、数字、下划线、短横线,至少 1 位。

  • @:固定匹配邮箱符号 @。

  • [a-zA-Z0-9_-]+:域名主体(如 qq、baidu)。

  • \.[a-zA-Z]{2,}:匹配后缀,如.com、.cn、.org,后缀至少 2 位字母。

匹配纯中文汉字

用于匹配纯中文汉字,正则如下:

^[\u4e00-\u9fa5]+$

说明:

  • \u4e00-\u9fa5:Unicode 编码中所有中文汉字范围。

  • []:字符集合,匹配范围内任意一个汉字。

  • +:匹配1 个及以上汉字。

  • ^ 和 $:整串只能是中文,不能夹杂字母、数字、符号。

6-16 位密码(字母 + 数字组合)

使用正则验证密码复杂度,密码包含字母和数字。正则如下:

^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,16}$

说明:

  • (?=.*[A-Za-z]):正向预查,必须包含至少 1 个字母。

  • (?=.*\d):必须包含至少 1 个数字。

  • [A-Za-z\d]{6,16}:只能由字母和数字组成,长度 6~16 位。

  • ^$:严格从头到尾匹配,不允许多余字符。

匹配 URL 网址

使用正则匹配 URL 地址,URL 地址以 http 和 https 开头,正则如下:

^(http|https)://.+

说明:

  • (http|https):匹配http或https协议。

  • ://:固定匹配网址协议分隔符。

  • .+:匹配后面任意字符(至少 1 个),代表域名、路径、参数等全部内容。

re 模块

为了在 Python 中支持正则表达式,内置了 re 模块。该模块用于字符串匹配、查找、替换、分割等高级文本处理,是爬虫、数据清洗、日志解析的核心工具。

模块核心方法

re.match()

该函数将从字符串开头匹配,只匹配开头,开头不满足直接返回 None,匹配成功返回匹配对象,失败返回 None。例如:

import re

# 待匹配的文本
text = "hello123python"
# 正则表达式模式
pattern = r"hello"

# 从开头匹配
result = re.match(pattern, text)
if result:
    print("匹配成功:", result.group())  # 获取匹配内容
else:
    print("匹配失败")

运行结果:

匹配成功: hello

re.search()

将扫描整个字符串(全局搜索第一个匹配),找到第一个符合规则的内容,比 match 更常用(不限制开头)。例如:

import re

# 待匹配的文本
text = "abc123def456"
# 规则:匹配连续数字
pattern = r"\d+"

# 搜索第一个匹配
result = re.search(pattern, text)
if result:
    print("找到数字:", result.group())  # 输出 123
else:
    print("未找到数字")

运行结果:

找到数字: 123

re.findall()

返回所有匹配结果(列表),没有匹配,返回空列表 []。例如:

import re

# 待匹配的文本
text = "价格:99元,折扣:8折,库存:100件"
# 正则表达式模式
pattern = r"\d+"  # 匹配所有数字

# 提取所有数字
result = re.findall(pattern, text)
print("所有数字:", result)  # ['99', '8', '100']

运行结果:

所有数字: ['99', '8', '100']

re.sub()

字符串替换,类似字符串 replace(),但支持正则规则替换。例如:

import re

text = "a1b2c3d4"
pattern = r"\d"  # 匹配所有数字

# 把所有数字替换成 *
new_text = re.sub(pattern, "*", text)
print(new_text)  # a*b*c*d*

re.split()

按规则分割,比 str.split() 强大得多,例如:

import re

text = "python,java;php c++|go"
pattern = r"[,; |]"  # 按 逗号、分号、空格、竖线 分割

result = re.split(pattern, text)
print(result)  # ['python', 'java', 'php', 'c++', 'go']

匹配对象

使用正则表达式模块的 search() 方法搜索字符串后,会返回一个匹配对象,该对象包含了匹配的内容,匹配内容的位置元组、开始索引和结束索引。

可以使用如下方法访问:

  • result.group()    获取匹配的字符串

  • result.start()    匹配开始索引

  • result.end()    匹配结束索引

  • result.span()    匹配位置元组 (start, end)

示例:

import re

# 待匹配的文本
text = "我的电话:13812345678,邮箱:abc@163.com"
# 找手机号
phone = re.search(r'1\d{10}', text)
if phone:
    print("手机号:", phone.group())  # 手机号: 13812345678
    print(phone.span())  # 匹配位置元组 (start, end),结果:(5, 16)
    print(phone.start())  # 匹配开始索引,结果:5
    print(phone.end())   # 匹配结束索引,结果:16
else:
    print('没有找到')

分组提取

在正则表达式中,可以使用 () 把想要的内容单独提取出来。例如,从一个字符串中提取姓名和电话,代码如下:

import re

text = "姓名:张三,电话:13812345678"
# 分组:(\w+) 姓名,(\d{11}) 手机号
pattern = r"姓名:(\w+),电话:(\d{11})"

result = re.search(pattern, text)
if result:
    print("姓名:", result.group(1))
    print("电话:", result.group(2))

如果在进行替换操作时,如何引用我们定义的分组呢?在非 Python 中,通常使用 $n 的方式引用,如第一个分组使用 $1 引用,

let str = "姓名:张三";
str = str.replace(/^(.+):(.+)$/g, "$2($1)");
console.log(str); // 张三(姓名)

而在 Python 中略有区别,使用如下两种方式引用分组:

(1)用“\数字”引用分组,例如:

  • \1 → 第 1 个分组

  • \2 → 第 2 个分组

(2)用“\g<数字>”引用分组,更安全,不会歧义,例如:

  • \g<1> → 第 1 个分组

  • \g<2> → 第 2 个分组

示例:把“姓名:张三”改成“张三(姓名)”

import re

text = "姓名:张三"
result = re.sub(r"(\w+):(\w+)", r"\2(\1)", text)
print(result) # 张三(姓名)

改用“\g<数字>”方式引用:

import re

text = "姓名:张三"
result = re.sub(r"(\w+):(\w+)", r"\g<2>(\g<1>)", text)
print(result) # 张三(姓名)

预编译

上面我们使用的正则表达式都是直接将正则表达式字符串直接传递给 re 模块对应的方法,如果有 100 个字符串待处理,按照上面的方式,正则表达式字符串需要编译 100 次,这效率有点低。能不能在处理 100 个字符串前,先编译正则表达式,然后再进行处理。这样就减少了 99 次正则表达式编译,提升效率。

在 Python 的 re 模块中,可以使用 re.compile() 方法预先编译正则表达式,然后使用编译后的正则表达式进行文本处理。例如:

import re

# 预编译正则规则(只编译一次,后续反复复用)
pattern = re.compile(r"\d+")

# 准备 10 个待处理的字符串
text_list = [
    "123abc",
    "def456",
    "hello789",
    "0python99",
    "test_666",
    "888end",
    "no_number",
    "520love",
    "1314life",
    "6789xyz"
]

# 迭代遍历 + 批量处理
for idx, text in enumerate(text_list, 1):  # idx 是序号,从1开始
    result = pattern.findall(text)  # 复用预编译的 pattern
    print(f"第{idx}个字符串: {text:<12} → 提取数字: {result}")

运行结果:

第1个字符串: 123abc       → 提取数字: ['123']
第2个字符串: def456       → 提取数字: ['456']
第3个字符串: hello789     → 提取数字: ['789']
第4个字符串: 0python99    → 提取数字: ['0', '99']
第5个字符串: test_666     → 提取数字: ['666']
第6个字符串: 888end       → 提取数字: ['888']
第7个字符串: no_number    → 提取数字: []
第8个字符串: 520love      → 提取数字: ['520']
第9个字符串: 1314life     → 提取数字: ['1314']
第10个字符串: 6789xyz      → 提取数字: ['6789']

综合实战

前面已经学习了如何使用 re 模块进行正则表达式应用,通过正则表达式提取、替换文本。下面通过一个综合示例来演示正则表达式的应用,该示例将实现用户信息解析,从一段文本中提取用户信息,功能如下:

(1)所有邮箱提取

(2)所有手机号提取

(3)所有身份证号提取

(3)替换敏感信息为 ***

示例代码:

import re

def parse_user_info(text):
    print("=== 原始文本 ===")
    print(text)

    # 提取所有的邮箱
    email_pattern = r"\w+@\w+\.\w+"
    emails = re.findall(email_pattern, text)

    # 提取所有的手机号(11位)
    phone_pattern = r"1[3-9]\d{9}"
    phones = re.findall(phone_pattern, text)

    # 提取所有的身份证(18位)
    id_pattern = r"\d{18}|\d{17}X"
    ids = re.findall(id_pattern, text)

    # 身份证脱敏
    desensitize_pattern = r"(\d{6})\d{8}(\d{3}[\dX])"
    desensitized = re.sub(desensitize_pattern, (r"\1" + "*" * 8 + r"\2"), text)
    
    # 手机号脱敏
    desensitize_pattern = r"(1[3-9]\d)\d{4}(\d{4})"
    desensitized = re.sub(desensitize_pattern, (r"\1" + "*" * 4 + r"\2"), desensitized)

    # 输出结果
    print("\n=== 解析结果 ===")
    print("邮箱:", emails)
    print("手机号:", phones)
    print("身份证:", ids)
    print("\n=== 脱敏后文本 ===")
    print(desensitized)


if __name__ == "__main__":
    # 测试文本
    text = """
姓名:小明
邮箱:xiaoming@qq.com
电话:13812345678
身份证:110101199001011234
姓名:小红
邮箱:xiaohong@163.com
电话:13998765432
身份证:31010119951212123X
    """
    parse_user_info(text)

运行结果:

=== 原始文本 ===

姓名:小明
邮箱:xiaoming@qq.com
电话:13812345678
身份证:110101199001011234
姓名:小红
邮箱:xiaohong@163.com
电话:13998765432
身份证:31010119951212123X
    

=== 解析结果 ===
邮箱: ['xiaoming@qq.com', 'xiaohong@163.com']
手机号: ['13812345678', '19900101123', '13998765432', '19951212123']
身份证: ['110101199001011234', '31010119951212123X']

=== 脱敏后文本 ===

姓名:小明
邮箱:xiaoming@qq.com
电话:138****5678
身份证:110101********1234
姓名:小红
邮箱:xiaohong@163.com
电话:139****5432
身份证:310101********123X

关于更多 re 模块的知识,请参考 https://docs.python.org/3/library/re.html 文档。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号