1. 什么是正则表达式

正则表达式(Regular Expression,简称regex或regexp)是一种用于匹配字符串的模式。它可以用来:

  • 验证输入格式(如邮箱、手机号等)
  • 提取特定格式的文本
  • 替换文本内容
  • 分割字符串

2. Python中的re模块

Python内置了re模块来处理正则表达式:

import re

2.1 re模块主要函数

函数功能返回值

re.match()

从字符串开头匹配

Match对象或None

re.search()

搜索整个字符串

Match对象或None

re.findall()

找到所有匹配项

列表

re.finditer()

找到所有匹配项(迭代器)

迭代器

re.sub()

替换匹配项

字符串

re.split()

分割字符串

列表

3. 正则表达式基础语法

3.1 基本字符匹配

import re

# 直接匹配字符
pattern = r"hello"
text = "hello world"
result = re.match(pattern, text)
print(result.group())  # 输出: hello

3.2 特殊字符(元字符)

字符含义示例

.

匹配任意字符(除换行符)

a.b匹配 "a" + 任意字符 + "b"

^

匹配字符串开头

^hello匹配以hello开头的字符串

$

匹配字符串结尾

world$匹配以world结尾的字符串

*

匹配前一个字符0次或多次

ab*匹配 "a" + 0个或多个"b"

+

匹配前一个字符1次或多次

ab+匹配 "a" + 1个或多个"b"

?

匹配前一个字符0次或1次

ab?匹配 "a" + 0个或1个"b"

{n}

匹配前一个字符n次

a{3}匹配 "aaa"

{n,}

匹配前一个字符至少n次

a{2,}匹配至少2个"a"

{n,m}

匹配前一个字符n到m次

a{2,4}匹配2到4个"a"

3.3 字符类

字符类含义示例

[abc]

匹配方括号中的任意一个字符

[abc]匹配 "a" 或 "b" 或 "c"

[^abc]

匹配除方括号中字符外的任意字符

[^abc]匹配非a、b、c的字符

[a-z]

匹配小写字母

[a-z]匹配任意小写字母

[A-Z]

匹配大写字母

[A-Z]匹配任意大写字母

[0-9]

匹配数字

[0-9]匹配任意数字

3.4 预定义字符类

字符类含义等价写法

\d

数字字符

[0-9]

\D

非数字字符

[^0-9]

\w

单词字符(字母、数字、下划线)

[a-zA-Z0-9_]

\W

非单词字符

[^a-zA-Z0-9_]

\s

空白字符(空格、制表符、换行符等)

[ \t\n\r\f\v]

\S

非空白字符

[^ \t\n\r\f\v]

3.5 转义字符

当需要匹配特殊字符本身时,需要使用反斜杠转义:

# 匹配点号
pattern = r"\."
text = "file.txt"
result = re.search(pattern, text)
print(result.group())  # 输出: .

4. 常用re函数详解

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

import re

# match只从字符串开头开始匹配
text = "hello world"
pattern = r"hello"

result = re.match(pattern, text)
if result:
    print("匹配成功:", result.group())  # 输出: 匹配成功: hello
else:
    print("匹配失败")

# 如果不从开头匹配,则返回None
text2 = "say hello world"
result2 = re.match(pattern, text2)
print(result2)  # 输出: None

4.2 re.search() - 全局搜索

import re

# search搜索整个字符串
text = "say hello world"
pattern = r"hello"

result = re.search(pattern, text)
if result:
    print("找到匹配:", result.group())  # 输出: 找到匹配: hello
    print("匹配位置:", result.span())   # 输出: 匹配位置: (4, 9)

4.3 re.findall() - 找到所有匹配

import re

# findall返回所有匹配项组成的列表
text = "phone: 138-1234-5678, mobile: 159-8765-4321"
pattern = r"\d{3}-\d{4}-\d{4}"

matches = re.findall(pattern, text)
print(matches)  # 输出: ['138-1234-5678', '159-8765-4321']

4.4 re.finditer() - 返回迭代器

import re

# finditer返回Match对象的迭代器,节省内存
text = "phone: 138-1234-5678, mobile: 159-8765-4321"
pattern = r"\d{3}-\d{4}-\d{4}"

for match in re.finditer(pattern, text):
    print(f"匹配内容: {match.group()}, 位置: {match.span()}")

4.5 re.sub() - 替换匹配项

import re

# sub用于替换匹配的文本
text = "我的电话是138-1234-5678"
pattern = r"\d{3}-\d{4}-\d{4}"
replacement = "****-****-****"

new_text = re.sub(pattern, replacement, text)
print(new_text)  # 输出: 我的电话是****-****-****

# 使用函数进行复杂替换
def mask_phone(match):
    phone = match.group()
    return phone[:3] + "****" + phone[-4:]

text2 = "联系我: 138-1234-5678 或 159-8765-4321"
result = re.sub(pattern, mask_phone, text2)
print(result)  # 输出: 联系我: 138****5678 或 159****4321

4.6 re.split() - 分割字符串

import re

# split使用正则表达式分割字符串
text = "apple,banana;orange:grape"
pattern = r"[,;:]"

fruits = re.split(pattern, text)
print(fruits)  # 输出: ['apple', 'banana', 'orange', 'grape']

# 带捕获组的分割
text2 = "apple123banana456orange"
pattern2 = r"(\d+)"
result = re.split(pattern2, text2)
print(result)  # 输出: ['apple', '123', 'banana', '456', 'orange']

5. 高级特性

5.1 分组和捕获

import re

# 使用括号创建分组
text = "John Doe, age 30"
pattern = r"(\w+)\s+(\w+),\s+age\s+(\d+)"

match = re.search(pattern, text)
if match:
    print("完整匹配:", match.group(0))  # 输出: John Doe, age 30
    print("第一个分组:", match.group(1))  # 输出: John
    print("第二个分组:", match.group(2))  # 输出: Doe
    print("第三个分组:", match.group(3))  # 输出: 30
    print("所有分组:", match.groups())    # 输出: ('John', 'Doe', '30')

5.2 命名分组

import re

# 使用命名分组,提高代码可读性
text = "John Doe, age 30"
pattern = r"(?P<first_name>\w+)\s+(?P<last_name>\w+),\s+age\s+(?P<age>\d+)"

match = re.search(pattern, text)
if match:
    print("姓名:", match.group('first_name'), match.group('last_name'))
    print("年龄:", match.group('age'))
    print("所有组:", match.groupdict())

5.3 编译正则表达式

import re

# 预编译正则表达式提高性能
pattern = re.compile(r"\d{3}-\d{4}-\d{4}")

text1 = "phone: 138-1234-5678"
text2 = "mobile: 159-8765-4321"

# 使用编译后的模式
result1 = pattern.search(text1)
result2 = pattern.search(text2)
print(result1.group())  # 输出: 138-1234-5678
print(result2.group())  # 输出: 159-8765-4321

5.4 标志参数

import re

# re.IGNORECASE - 忽略大小写
text = "Hello World"
pattern = r"hello"
result = re.search(pattern, text, re.IGNORECASE)
print(result.group())  # 输出: Hello

# re.MULTILINE - 多行模式
text = """first line
second line
third line"""
pattern = r"^second"
# 不使用MULTILINE标志,匹配失败
result1 = re.search(pattern, text)
print(result1)  # 输出: None

# 使用MULTILINE标志
result2 = re.search(pattern, text, re.MULTILINE)
print(result2.group())  # 输出: second

# re.DOTALL - 让.匹配包括换行符在内的所有字符
text = "hello\nworld"
pattern = r"hello.world"
result1 = re.search(pattern, text)  # 不匹配
result2 = re.search(pattern, text, re.DOTALL)  # 匹配
print(result2.group())  # 输出: hello
                      #       world

6. 实战应用示例

6.1 邮箱验证

import re

def validate_email(email):
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

# 测试
emails = [
    "user@example.com",
    "test.email+tag@domain.co.uk",
    "invalid.email",
    "user@domain"
]

for email in emails:
    print(f"{email}: {'有效' if validate_email(email) else '无效'}")

6.2 手机号验证

import re

def validate_phone(phone):
    # 中国手机号验证
    pattern = r'^1[3-9]\d{9}$'
    return bool(re.match(pattern, phone))

phones = ["13812345678", "15987654321", "12345678901", "1381234567"]
for phone in phones:
    print(f"{phone}: {'有效' if validate_phone(phone) else '无效'}")

6.3 提取HTML标签

import re

html = '<div class="container"><p>Hello <strong>World</strong></p></div>'
# 提取所有标签名
tag_pattern = r'<(\w+)'
tags = re.findall(tag_pattern, html)
print("标签:", tags)  # 输出: ['div', 'p', 'strong']

# 提取标签内容
content_pattern = r'<[^>]*>([^<]*)</[^>]*>'
contents = re.findall(content_pattern, html)
print("内容:", contents)  # 输出: ['Hello ', 'World']

6.4 日志分析

import re

log_line = '192.168.1.1 - - [23/Nov/2025:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234'

# 解析Apache日志格式
log_pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[([^\]]+)\] "(\w+) ([^"]+)" (\d+) (\d+)'

match = re.match(log_pattern, log_line)
if match:
    ip, timestamp, method, url, status, size = match.groups()
    print(f"IP: {ip}")
    print(f"时间: {timestamp}")
    print(f"方法: {method}")
    print(f"URL: {url}")
    print(f"状态码: {status}")
    print(f"大小: {size}")

7. 贪婪与非贪婪匹配

7.1 基础概念对比

对比项贪婪匹配非贪婪匹配

定义

默认行为,尽可能多地匹配字符

在量词后加?,尽可能少地匹配字符

匹配策略

贪得无厌,匹配到最长可能的字符串

适可而止,匹配到最短必需的字符串

符号表示

*,+,?,{n,m}

*?,+?,??,{n,m}?

优先级

高(默认)

需要显式指定

7.2 量词对比表

量词类型贪婪形式非贪婪形式含义

零次或多次

*

*?

匹配0个或更多,尽可能多/少

一次或多次

+

+?

匹配1个或更多,尽可能多/少

零次或一次

?

??

匹配0个或1个,尽可能多/少

确切次数

{n,m}

{n,m}?

匹配n到m次,尽可能多/少

7.3 实际示例对比

示例文本模式贪婪匹配结果非贪婪匹配结果说明

"abc123def456"

a.*\d

["abc123def456"]

["abc123"]

贪婪匹配到最远数字,非贪婪匹配到最近数字

"<div>content</div><span>text</span>"

<.*>

["<div>content</div><span>text</span>"]

["<div>", "</div>", "<span>", "</span>"]

贪婪匹配整个范围,非贪婪匹配单个标签

"\"hello\" and \"world\""

"(.*)"

["hello\" and \"world"]

["hello", "world"]

贪婪匹配跨越引号,非贪婪分别匹配

"aaaa"

a+

["aaaa"]

["a", "a", "a", "a"]

贪婪匹配全部,非贪婪逐个匹配

"aaaa"

a*

["aaaa", ""]

["", "", "", "", ""]

贪婪匹配非空,非贪婪匹配空串

7.4 应用场景对比

场景推荐匹配方式原因

提取HTML标签

非贪婪 (<.*?>)

避免匹配整个文档

提取引号内容

非贪婪 ("(.*?)")

避免跨越多个引号对

验证格式

贪婪

通常格式验证不需要精确控制

搜索关键词

贪婪

通常需要完整匹配

日志解析

非贪婪

避免跨越日志条目边界

数字提取

贪婪

通常需要完整数字串

7.5 问题示例与解决方案

import re

text = "<div>content</div><span>text</span>"
# 贪婪匹配(默认)
greedy = re.findall(r'<.*>', text)
print(greedy)  # ['<div>content</div><span>text</span>']

# 非贪婪匹配
non_greedy = re.findall(r'<.*?>', text)
print(non_greedy)  # ['<div>', '</div>', '<span>', '</span>']
Logo

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

更多推荐