pin_drop当前位置:知识文库 ❯ 图文

Python标准库re:正则表达式完全指南

一、re模块概述

re 是Python标准库中用于正则表达式操作的核心模块。正则表达式是一种强大的文本匹配工具,能够高效地完成字符串的搜索、替换、提取和验证。无论是数据清洗、日志分析、表单验证还是文本挖掘,re模块都是Python开发者的利器。

re模块的核心操作包括:匹配(判断是否符合模式)、搜索(查找第一个匹配)、查找全部(找出所有匹配)和替换(替换匹配内容)。


二、常用正则语法

1. 字符匹配

代码示例

.          # 匹配除换行符外的任意单个字符
\d         # 匹配数字 [0-9]
\D         # 匹配非数字
\w         # 匹配字母数字下划线 [a-zA-Z0-9_]
\W         # 匹配非单词字符
\s         # 匹配空白字符(空格、制表符、换行符)
\S         # 匹配非空白字符
[abc]      # 字符集,匹配a、b或c中的任意一个
[^abc]     # 否定字符集,匹配除a、b、c外的字符

2. 量词(重复匹配)

代码示例

*          # 匹配0次或多次
+          # 匹配1次或多次
?          # 匹配0次或1次
{n}        # 精确匹配n次
{n,}       # 匹配至少n次
{n,m}      # 匹配n到m次

3. 位置锚点

代码示例

^          # 匹配字符串开头
$          # 匹配字符串结尾
\b         # 匹配单词边界
\B         # 匹配非单词边界
(?=...)    # 正向前瞻(后面跟着...)
(?!...)    # 负向前瞻(后面不跟...)

4. 分组与捕获

代码示例

(...)      # 捕获分组,匹配内容并保存
(?:...)    # 非捕获分组,匹配但不保存
(?P<name>...)  # 命名捕获组
\1         # 反向引用第1个捕获组
(?P=name)  # 引用命名捕获组

5. 常用模式速查

场景 正则表达式 说明
手机号 1[3-9]\d{9} 中国大陆11位手机号
邮箱 [\w.-]+@[\w.-]+\.\w+ 基础邮箱格式匹配
身份证号 \d{17}[\dXx] 18位身份证号
URL https?://[\w./\-]+ 匹配http/https链接
日期 \d{4}-\d{2}-\d{2} YYYY-MM-DD格式

三、核心函数用法

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

代码示例

import re

# match从字符串开头匹配
result = re.match(r'\d+', '123abc')
if result:
    print(f"匹配到: {result.group()}")  # 输出: 123

# 开头不是数字,匹配失败
result = re.match(r'\d+', 'abc123')
print(result)  # None

2. re.search() - 搜索第一个匹配

代码示例

import re

# search在整个字符串中搜索第一个匹配
result = re.search(r'\d+', 'abc123def456')
if result:
    print(f"匹配到: {result.group()}")  # 输出: 123
    print(f"位置: {result.span()}")      # 输出: (3, 6)

3. re.findall() - 查找所有匹配

代码示例

import re

# findall返回所有匹配结果的列表
text = "电话:13812345678,邮箱:test@example.com,手机:13987654321"

phones = re.findall(r'1[3-9]\d{9}', text)
print(f"手机号: {phones}")  # ['13812345678', '13987654321']

# 带分组时返回元组列表
result = re.findall(r'(\d{4})-(\d{2})-(\d{2})', '2026-06-28 和 2026-07-01')
print(f"日期: {result}")  # [('2026', '06', '28'), ('2026', '07', '01')]

4. re.sub() - 替换

代码示例

import re

text = "我的电话是13812345678,备用电话13987654321"

# 将手机号中间4位替换为****
result = re.sub(r'(1[3-9]\d)\d{4}(\d{4})', r'\1****\2', text)
print(result)  # 我的电话是138****5678,备用电话139****4321

# 使用函数作为替换
def mask_phone(match):
    phone = match.group()
    return phone[:3] + '****' + phone[7:]

result = re.sub(r'1[3-9]\d{9}', mask_phone, text)
print(result)

5. re.split() - 分割

代码示例

import re

text = "苹果,香蕉;橙子 葡萄、芒果"

# 按多种分隔符分割
result = re.split(r'[,;、\s]+', text)
print(result)  # ['苹果', '香蕉', '橙子', '葡萄', '芒果']

# 保留分隔符
result = re.split(r'([,;、\s]+)', text)
print(result)

6. re.compile() - 预编译正则

代码示例

import re

# 预编译正则表达式,提高重复使用时的效率
phone_pattern = re.compile(r'1[3-9]\d{9}')

text1 = "联系电话:13812345678"
text2 = "备用号码:13987654321"

print(phone_pattern.findall(text1))  # ['13812345678']
print(phone_pattern.findall(text2))  # ['13987654321']
print(phone_pattern.search(text1).group())  # 13812345678

四、完整代码示例

示例1:邮箱验证器

代码示例

import re

def validate_email(email):
    """验证邮箱格式是否正确"""
    pattern = r'^[\w.-]+@[\w.-]+\.\w+$'
    if re.match(pattern, email):
        return True, f"{email} 是有效的邮箱地址"
    else:
        return False, f"{email} 不是有效的邮箱地址"

# 测试
emails = ["test@example.com", "user.name@domain.cn", "invalid-email", "@no-local.com"]
for email in emails:
    is_valid, msg = validate_email(email)
    print(msg)

示例2:从HTML中提取链接

代码示例

import re

html = '''
<a href="https://www.example.com">首页</a>
<a href="/about">关于我们</a>
<a href="https://blog.example.com/post/1">文章1</a>
<a href="mailto:test@example.com">联系邮箱</a>
'''

# 提取所有href属性值
links = re.findall(r'href=["\']([^"\']+)["\']', html)
print("所有链接:")
for link in links:
    print(f"  {link}")

# 只提取http/https链接
http_links = re.findall(r'href=["\'](https?://[^"\']+)["\']', html)
print("\nHTTP链接:")
for link in http_links:
    print(f"  {link}")

示例3:日志解析器

代码示例

import re

log_text = """
[2026-06-28 10:30:45] INFO: 用户登录成功 user_id=1001
[2026-06-28 10:31:02] ERROR: 数据库连接失败 code=500
[2026-06-28 10:31:15] WARNING: 内存使用率过高 usage=85%
[2026-06-28 10:32:00] INFO: 数据备份完成 size=1.2GB
"""

# 解析日志
log_pattern = re.compile(r'\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.+)')

for line in log_text.strip().split('\n'):
    match = log_pattern.match(line)
    if match:
        time, level, message = match.groups()
        print(f"时间: {time}, 级别: {level}, 消息: {message}")

# 统计各级别日志数量
levels = re.findall(r'\] (\w+):', log_text)
from collections import Counter
print(f"\n日志级别统计: {dict(Counter(levels))}")

示例4:敏感信息脱敏

代码示例

import re

def desensitize(text):
    """对文本中的敏感信息进行脱敏处理"""
    # 手机号脱敏:138****5678
    text = re.sub(r'(1[3-9]\d)\d{4}(\d{4})', r'\1****\2', text)
    
    # 身份证号脱敏:110***********1234
    text = re.sub(r'(\d{3})\d{11}(\d{4})', r'\1***********\2', text)
    
    # 邮箱脱敏:t***@example.com
    def mask_email(match):
        email = match.group()
        local, domain = email.split('@')
        if len(local) > 1:
            masked = local[0] + '*' * (len(local) - 1)
        else:
            masked = '*'
        return f"{masked}@{domain}"
    
    text = re.sub(r'[\w.-]+@[\w.-]+\.\w+', mask_email, text)
    
    return text

# 测试
original = "用户张三,手机13812345678,邮箱zhangsan@example.com,身份证110101199001011234"
print(f"原始: {original}")
print(f"脱敏: {desensitize(original)}")

五、注意事项与最佳实践

注意1:正则表达式字符串前务必加 r 前缀(原始字符串),如 r'\d+'。否则反斜杠会被Python字符串转义,导致匹配失败。

注意2re.match() 只从字符串开头匹配,如果开头不匹配就返回None。如果需要在整个字符串中搜索,请使用 re.search()

注意3*+? 默认是贪婪匹配,会尽可能多地匹配字符。如需非贪婪匹配,在后面加 ?,如 .*?

注意4:对于需要重复使用的正则表达式,建议使用 re.compile() 预编译,可以提升匹配效率。

小贴士

编写复杂正则表达式时,可以使用 re.VERBOSE(或 re.X)标志,允许在正则中添加空白和注释,大幅提高可读性。例如:re.compile(r'\d{4} # 年 -\d{2} # 月', re.X)


六、小结

  • 核心函数match() 开头匹配、search() 搜索、findall() 查找全部、sub() 替换、split() 分割

  • 常用语法\d 数字、\w 单词字符、+ 一次以上、* 零次以上、() 分组

  • 原始字符串:正则前加 r 前缀避免转义问题

  • 贪婪与非贪婪:默认贪婪匹配,加 ? 变为非贪婪

  • 预编译:使用 re.compile() 提高重复使用效率


七、练习题

练习1

编写一个函数,验证用户输入的字符串是否符合中国大陆手机号格式(11位,以1开头,第二位为3-9)。

练习2

编写一个程序,从一段文本中提取所有的日期(格式:YYYY-MM-DD),并按时间顺序排序输出。

练习3

编写一个Markdown标题提取器,能够从Markdown文本中提取所有以#开头的标题行,并返回标题级别和标题内容。

常见问题

re.match和re.search有什么区别?

re.match() 只从字符串开头匹配,如果开头不匹配就返回None;re.search() 会扫描整个字符串,找到第一个匹配。如果需要验证字符串是否以某模式开头,用match;如果需要在任意位置查找,用search。

为什么正则前要加r前缀?

r 前缀表示原始字符串(raw string),这样反斜杠 \\ 不会被Python解释为转义字符。例如 r'\d' 表示匹配数字,而 '\d' 可能被解释为转义序列导致错误。

贪婪匹配和非贪婪匹配有什么区别?

贪婪匹配(默认)会尽可能多地匹配字符,如 .* 会匹配到最后一个可能的位置;非贪婪匹配(加 ?)会尽可能少地匹配,如 .*? 匹配到第一个满足条件的位置就停止。提取HTML标签内容时通常用非贪婪。

如何匹配中文字符?

使用 [\u4e00-\u9fa5] 匹配中文字符。例如 re.findall(r'[\u4e00-\u9fa5]+', text) 可以提取文本中的所有中文词语。注意 \w 在Python 3中默认支持Unicode,也能匹配中文。

如何处理正则表达式的特殊字符?

使用 re.escape() 函数可以自动转义字符串中的所有正则特殊字符。例如 re.escape('hello.world') 返回 'hello\\.world',这样点号就只匹配字面量点号而非任意字符。

标签: re 正则表达式 标准库 文本匹配 模式匹配 数据提取

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python标准库json使用 下一篇: Python标准库collections:高级容器类型

poll相关推荐