Python3 正则表达式

正则表达式(Regular Expression)是一种用于匹配、查找和处理字符串的强大工具,通过特殊符号(元字符)定义匹配规则,广泛应用于文本检索、数据清洗、格式验证等场景。Python3 通过内置的re模块提供了完整的正则表达式支持,本文从基础语法到实战案例,全面解析 Python 正则表达式的使用。

一、正则表达式基础:元字符与匹配规则

正则表达式的核心是 “元字符”—— 具有特殊含义的符号,用于描述字符的模式。掌握这些元字符是使用正则的基础。

1. 常用元字符(核心!必须掌握)

元字符含义示例
. 匹配任意单个字符(除换行符\n a.b 匹配 "aab"、"acb",不匹配 "ab"
* 匹配前面的字符 / 子模式0 次或多次(贪婪匹配) ab* 匹配 "a"、"ab"、"abb"、"abbb"
+ 匹配前面的字符 / 子模式1 次或多次 ab+ 匹配 "ab"、"abb",不匹配 "a"
? 匹配前面的字符 / 子模式0 次或 1 次 ab? 匹配 "a"、"ab",不匹配 "abb"
^ 匹配字符串开头(若在 [] 中则表示 “非”) ^abc 匹配 "abc123",不匹配 "xabc"
$ 匹配字符串结尾 abc$ 匹配 "123abc",不匹配 "abcx"
[] 字符集,匹配其中任意一个字符(可指定范围) [abc] 匹配 "a"、"b"、"c";[0-9] 匹配任意数字
() 分组,将子模式视为一个整体,可用于提取或重复 (ab)+ 匹配 "ab"、"abab"
| 逻辑 “或”,匹配左边或右边的模式 a|b 匹配 "a" 或 "b"
\d 匹配任意数字(等价于[0-9] \d{3} 匹配 "123"、"456"
\D 匹配非数字(等价于[^0-9] \D 匹配 "a"、"!"、" "
\w 匹配字母、数字、下划线(等价于[a-zA-Z0-9_] \w+ 匹配 "hello"、"user123"
\W 匹配非字母、数字、下划线(等价于[^a-zA-Z0-9_] \W 匹配 "!"、"@"、" "
\s 匹配空白字符(空格、制表符\t、换行符\n等) a\sb 匹配 "a b"、"a\tb"
\S 匹配非空白字符 a\Sb 匹配 "aab"、"a@b"
\b 单词边界(匹配单词开头或结尾,不匹配具体字符) \bhello\b 匹配 "hello",不匹配 "helloworld"

2. 量词:指定匹配次数

除了*/+/?,还可以用{m,n}精确指定匹配次数:

量词含义示例
{n} 匹配恰好 n 次 a{3} 匹配 "aaa"
{n,} 匹配至少 n 次 a{2,} 匹配 "aa"、"aaa"、"aaaa"
{n,m} 匹配n 到 m 次(包含 n 和 m) a{1,3} 匹配 "a"、"aa"、"aaa"

3. 贪婪匹配与非贪婪匹配

  • 贪婪匹配(默认):尽可能匹配最长的符合规则的字符串(如*/+/{n,m}默认贪婪)。
  • 非贪婪匹配:在量词后加?,尽可能匹配最短的符合规则的字符串。

示例:

import re

text = "aabbaabb"
# 贪婪匹配:.*会匹配从第一个a到最后一个b的最长字符串
print(re.match("a.*b", text).group())  # 输出:aabbaabb

# 非贪婪匹配:.*?会匹配从第一个a到最近的b的最短字符串
print(re.match("a.*?b", text).group())  # 输出:aabb
 

二、Python re模块:正则表达式的 “操作工具”

re模块提供了一系列函数,用于实现正则表达式的匹配、查找、替换等操作,核心函数如下:

1. re.match(pattern, string):从字符串开头匹配

  • 若匹配成功,返回Match对象;否则返回None
  • 注意:仅从字符串第一个字符开始匹配,不匹配则返回None(哪怕中间有符合规则的子串)。
 
import re

# 匹配以"hello"开头的字符串
result = re.match("hello", "hello world")
print(result.group())  # 输出:hello(匹配成功)

# 不匹配(字符串开头不是"hello")
result = re.match("hello", "hi hello")
print(result)  # 输出:None
 

2. re.search(pattern, string):在字符串任意位置查找第一个匹配

  • 扫描整个字符串,返回第一个符合规则的Match对象;无匹配则返回None
  • match的区别:search不限制从开头匹配。
 
import re

# 在任意位置查找第一个"world"
result = re.search("world", "hello world, welcome to the world")
print(result.group())  # 输出:world(只返回第一个匹配)
 

3. re.findall(pattern, string):查找所有匹配的子串

  • 返回一个列表,包含所有符合规则的子串(无匹配则返回空列表)。
 
import re

# 查找所有数字
text = "age: 25, score: 90, weight: 65"
numbers = re.findall(r"\d+", text)  # r前缀表示原始字符串,避免转义问题
print(numbers)  # 输出:['25', '90', '65']
 

4. re.finditer(pattern, string):查找所有匹配,返回迭代器

  • findall类似,但返回Match对象的迭代器(适合处理大量匹配结果,节省内存)。
 
import re

text = "a:1, b:2, c:3"
# 查找所有"字母:数字"格式的子串
iter_result = re.finditer(r"(\w):(\d)", text)
for match in iter_result:
    print(match.group())  # 输出:a:1、b:2、c:3
    print(f"字母:{match.group(1)}, 数字:{match.group(2)}")  # 提取分组内容
 

5. re.sub(pattern, repl, string):替换匹配的子串

  • 将字符串中所有符合规则的子串替换为repl(可以是字符串或函数),返回替换后的新字符串。

示例:

import re

# 将所有数字替换为"*"
text = "密码:123456,验证码:654321"
new_text = re.sub(r"\d+", "*", text)
print(new_text)  # 输出:密码:*,验证码:*

# 用函数动态替换(将数字加1)
def add_one(match):
    return str(int(match.group()) + 1)

text = "a:1, b:2"
new_text = re.sub(r"\d+", add_one, text)
print(new_text)  # 输出:a:2, b:3
 

6. re.split(pattern, string):按匹配的子串分割字符串

  • 以符合规则的子串为分隔符,将字符串分割成列表。
 
import re

# 按逗号或空格分割
text = "apple, banana orange; grape"
parts = re.split(r"[,; ]", text)  # 匹配逗号、分号或空格
print(parts)  # 输出:['apple', '', 'banana', 'orange', '', 'grape']
 

7. re.compile(pattern):编译正则表达式(提高效率)

  • 将正则表达式字符串编译为RegexObject对象,可重复使用(多次调用时效率更高)。

示例:
import re

# 编译正则(只做一次)
pattern = re.compile(r"\d+")

# 重复使用编译后的对象
text1 = "score: 90"
text2 = "age: 25"
print(pattern.findall(text1))  # ['90']
print(pattern.findall(text2))  # ['25']
 

三、分组与捕获:提取特定内容

通过()定义分组,可以从匹配结果中提取特定部分,这是正则的核心应用之一。

1. 基础分组:group()groups()

  • match.group(n):返回第 n 个分组的内容(n=0 表示整个匹配,n=1 开始是分组)。
  • match.groups():返回所有分组内容的元组。

示例:
 
import re

text = "张三, 年龄:25, 性别:男"
# 分组匹配:姓名、年龄、性别
pattern = r"(\w+), 年龄:(\d+), 性别:(\w+)"
match = re.search(pattern, text)

print(match.group(0))  # 整个匹配:张三, 年龄:25, 性别:男
print(match.group(1))  # 第1组(姓名):张三
print(match.group(2))  # 第2组(年龄):25
print(match.groups())  # 所有分组:('张三', '25', '男')
 

2. 命名分组:(?P<name>pattern)(更易读)

给分组命名,通过group("name")提取,避免记索引。
 
import re

text = "邮箱: user@example.com"
# 命名分组:用户名(name)和域名(domain)
pattern = r"邮箱: (?P<name>\w+)@(?P<domain>\w+\.\w+)"
match = re.search(pattern, text)

print(match.group("name"))  # user
print(match.group("domain"))  # example.com
 

四、实战案例:正则表达式的典型应用

1. 验证邮箱格式

 
import re

def is_valid_email(email):
    # 邮箱规则:用户名@域名(域名含.和字母)
    pattern = r"^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
    return re.match(pattern, email) is not None

print(is_valid_email("user@example.com"))  # True
print(is_valid_email("user.name+tag@example.co.uk"))  # True
print(is_valid_email("invalid-email"))  # False
 

2. 提取 URL 中的域名

import re

url = "https://www.example.com/path?query=123"
# 匹配域名(www.xxx.com 或 xxx.com)
pattern = r"https?://(www\.)?([a-zA-Z0-9-]+\.[a-zA-Z]+)"
match = re.search(pattern, url)
print(match.group(2))  # example.com
 

3. 清洗文本中的特殊字符

import re

text = "这是一段包含【特殊字符】、<标签>和\n换行的文本!"
# 移除所有非中文字符、字母、数字和空格
clean_text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9 ]", "", text)
print(clean_text)  # 这是一段包含特殊字符和换行的文本
 

五、避坑指南(新手常犯错误)

  1. 忘记^$导致部分匹配
    验证格式时(如手机号),若不加^$,可能匹配到包含目标模式的长字符串。
    错误示例:re.match(r"\d{11}", "13800138000abc") 会返回匹配(实际想验证 11 位纯数字)。
    正确做法:re.match(r"^\d{11}$", "13800138000")(确保整个字符串都是 11 位数字)。
  2. 转义字符处理不当
    正则中\是转义符(如\d表示数字),但 Python 字符串中\也是转义符,需用r前缀定义 “原始字符串” 避免冲突。
    错误示例:re.match("\d+", "123")(Python 会将\d解析为转义符,实际应写成r"\d+")。
    正确做法:re.match(r"\d+", "123")
  3. 贪婪匹配导致结果不符合预期
    例如提取 HTML 标签<div>内容</div>,用<.*>会匹配整个<div>内容</div>(贪婪),而想要匹配<div>需用非贪婪模式<.*?>
  4. 过度依赖正则处理复杂场景
    正则适合简单的文本匹配,复杂场景(如 HTML/XML 解析)建议用专用库(如BeautifulSoup),避免正则逻辑过于复杂难以维护。

六、常用正则模板(直接复用)

场景正则表达式(Python 格式)
手机号 r"^1[3-9]\d{9}$"
身份证号 r"^\d{17}[\dXx]$"
邮箱 r"^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
整数 / 小数 r"^-?\d+(\.\d+)?$"
URL r"^https?://[^\s]+$"
中文 r"[\u4e00-\u9fa5]"

总结

Python 正则表达式的核心是re模块和元字符系统:

  • 元字符定义匹配规则(如\d匹配数字,*匹配多次);
  • re模块函数实现具体操作(match/search查找,findall提取,sub替换);
  • 分组功能用于精准提取特定内容,命名分组提高可读性;
  • 实际使用中需注意贪婪 / 非贪婪匹配、转义字符和边界匹配。

掌握正则表达式能极大提升文本处理效率,建议结合实际场景多练习,逐步熟悉各种元字符的组合用法

posted on 2025-11-12 08:37  小陶coding  阅读(36)  评论(0)    收藏  举报