Python 正则表达式
正则表达式(Regular Expression,简称 regex 或 regexp)是一种强大的文本模式匹配工具。它使用特殊的语法来描述和匹配字符串中的模式,广泛应用于文本搜索、替换、验证和提取等场景。在Python中,正则表达式通过内置的re模块来实现。
正则表达式看似复杂,实则很简单,如果大家在学习的过程中有疑惑的,可以使用作者开发的正则表达式可使用工具,它能实时可视化你写的正则是匹配到哪些字符了

基础语法和元字符
普通字符
普通字符就是字面意思,直接匹配对应的字符。
import re
text = "Hello World"
pattern = "Hello"
result = re.search(pattern, text)
print(result.group()) # 输出: Hello
常用元字符
元字符 | 含义 | 示例 |
---|---|---|
. |
匹配除换行符外的任意字符 | a.c 匹配 "abc", "a1c" |
^ |
匹配字符串开头 | ^Hello 匹配以"Hello"开头的字符串 |
$ |
匹配字符串结尾 | end$ 匹配以"end"结尾的字符串 |
* |
匹配前面字符0次或多次 | ab* 匹配 "a", "ab", "abbb" |
+ |
匹配前面字符1次或多次 | ab+ 匹配 "ab", "abbb",不匹配 "a" |
? |
匹配前面字符0次或1次 | ab? 匹配 "a", "ab" |
\ |
转义字符 | \. 匹配字面意思的点号 |
import re
# 点号匹配任意字符
text = "cat bat rat"
pattern = r".at"
matches = re.findall(pattern, text)
print(matches) # 输出: ['cat', 'bat', 'rat']
# 开头和结尾匹配
text = "Hello World"
print(re.search(r"^Hello", text) is not None) # True
print(re.search(r"World$", text) is not None) # True
# 量词使用
text = "a ab abb abbb"
print(re.findall(r"ab*", text)) # ['a', 'ab', 'abb', 'abbb']
print(re.findall(r"ab+", text)) # ['ab', 'abb', 'abbb']
print(re.findall(r"ab?", text)) # ['a', 'ab', 'ab', 'ab']
字符类
import re
text = "Hello123 World456"
# \d 匹配数字
numbers = re.findall(r"\d", text)
print(numbers) # ['1', '2', '3', '4', '5', '6']
# \w 匹配字母、数字、下划线
words = re.findall(r"\w+", text)
print(words) # ['Hello123', 'World456']
# \s 匹配空白字符
spaces = re.findall(r"\s", text)
print(spaces) # [' ']
# 自定义字符类
vowels = re.findall(r"[aeiou]", "Hello World")
print(vowels) # ['e', 'o', 'o']
# 字符范围
letters = re.findall(r"[a-z]+", "Hello World 123")
print(letters) # ['ello', 'orld']
Python中的re模块
导入和基本使用
import re
# 基本匹配
text = "Python是一门编程语言"
pattern = r"Python"
if re.search(pattern, text):
print("找到匹配")
else:
print("未找到匹配")
re模块的主要函数
re.search() - 查找第一个匹配
import re
text = "联系电话:138-1234-5678,备用电话:139-8765-4321"
pattern = r"\d{3}-\d{4}-\d{4}"
match = re.search(pattern, text)
if match:
print(f"找到电话号码: {match.group()}") # 138-1234-5678
print(f"位置: {match.start()}-{match.end()}") # 位置: 4-16
re.match() - 从字符串开头匹配
import re
text = "Python编程"
# match只从字符串开头匹配
result1 = re.match(r"Python", text)
print(result1.group() if result1 else "未匹配") # Python
result2 = re.match(r"编程", text)
print(result2.group() if result2 else "未匹配") # 未匹配
re.findall() - 查找所有匹配
import re
text = "今天是2024年1月15日,明天是2024年1月16日"
dates = re.findall(r"\d{4}年\d{1,2}月\d{1,2}日", text)
print(dates) # ['2024年1月15日', '2024年1月16日']
# 返回元组(有分组时)
emails = "联系邮箱:user1@example.com,备用邮箱:user2@gmail.com"
email_parts = re.findall(r"(\w+)@(\w+\.\w+)", emails)
print(email_parts) # [('user1', 'example.com'), ('user2', 'gmail.com')]
re.finditer() - 返回匹配对象的迭代器
import re
text = "价格:¥100,折扣价:¥80,VIP价:¥60"
pattern = r"¥(\d+)"
for match in re.finditer(pattern, text):
print(f"价格: {match.group(1)}, 位置: {match.start()}-{match.end()}")
# 输出:
# 价格: 100, 位置: 2-6
# 价格: 80, 位置: 11-14
# 价格: 60, 位置: 20-23
re.sub() - 替换匹配的内容
import re
# 基本替换
text = "今天天气很好,明天天气也不错"
new_text = re.sub(r"天气", "心情", text)
print(new_text) # 今天心情很好,明天心情也不错
# 使用函数进行替换
def price_converter(match):
price = int(match.group(1))
return f"${price * 0.15:.2f}"
text = "商品价格:¥100,特价:¥80"
converted = re.sub(r"¥(\d+)", price_converter, text)
print(converted) # 商品价格:$15.00,特价:$12.00
re.split() - 按模式分割字符串
import re
# 基本分割
text = "苹果,香蕉;橙子|葡萄"
fruits = re.split(r"[,;|]", text)
print(fruits) # ['苹果', '香蕉', '橙子', '葡萄']
# 保留分隔符
text = "第一章。第二章!第三章?"
chapters = re.split(r"([。!?])", text)
print(chapters) # ['第一章', '。', '第二章', '!', '第三章', '?', '']
分组和捕获
基本分组
import re
# 使用圆括号创建分组
text = "我的生日是1990年5月20日"
pattern = r"(\d{4})年(\d{1,2})月(\d{1,2})日"
match = re.search(pattern, text)
if match:
print(f"完整匹配: {match.group(0)}") # 1990年5月20日
print(f"年份: {match.group(1)}") # 1990
print(f"月份: {match.group(2)}") # 5
print(f"日期: {match.group(3)}") # 20
print(f"所有分组: {match.groups()}") # ('1990', '5', '20')
命名分组
import re
text = "张三的邮箱是zhangsan@example.com"
pattern = r"(?P<name>\w+)的邮箱是(?P<email>\w+@\w+\.\w+)"
match = re.search(pattern, text)
if match:
print(f"姓名: {match.group('name')}") # 张三
print(f"邮箱: {match.group('email')}") # zhangsan@example.com
print(f"字典形式: {match.groupdict()}") # {'name': '张三', 'email': 'zhangsan@example.com'}
非捕获分组
import re
# (?:...) 表示非捕获分组,不会被保存
text = "http://www.example.com 和 https://www.google.com"
pattern = r"(?:http|https)://(\w+\.\w+\.\w+)"
matches = re.findall(pattern, text)
print(matches) # ['www.example.com', 'www.google.com']
# 对比:使用捕获分组
pattern_with_capture = r"(http|https)://(\w+\.\w+\.\w+)"
matches_with_capture = re.findall(pattern_with_capture, text)
print(matches_with_capture) # [('http', 'www.example.com'), ('https', 'www.google.com')]
贪婪匹配与非贪婪匹配
贪婪匹配(默认行为)
import re
text = '<div>内容1</div><div>内容2</div>'
# 贪婪匹配:尽可能多地匹配
greedy_pattern = r"<div>.*</div>"
greedy_match = re.search(greedy_pattern, text)
print(f"贪婪匹配: {greedy_match.group()}")
# 输出: <div>内容1</div><div>内容2</div>
非贪婪匹配
import re
text = '<div>内容1</div><div>内容2</div>'
# 非贪婪匹配:尽可能少地匹配
non_greedy_pattern = r"<div>.*?</div>"
non_greedy_matches = re.findall(non_greedy_pattern, text)
print(f"非贪婪匹配: {non_greedy_matches}")
# 输出: ['<div>内容1</div>', '<div>内容2</div>']
量词的贪婪与非贪婪形式
import re
text = "aaaa"
print("贪婪量词:")
print(re.findall(r"a+", text)) # ['aaaa']
print(re.findall(r"a*", text)) # ['aaaa', '']
print(re.findall(r"a{2,4}", text)) # ['aaaa']
print("\n非贪婪量词:")
print(re.findall(r"a+?", text)) # ['a', 'a', 'a', 'a']
print(re.findall(r"a*?", text)) # ['', '', '', '', '']
print(re.findall(r"a{2,4}?", text)) # ['aa']
编译正则表达式
为什么要编译
当需要多次使用同一个正则表达式时,预先编译可以提高性能。
import re
# 编译正则表达式
phone_pattern = re.compile(r"(\d{3})-(\d{4})-(\d{4})")
text1 = "我的电话是138-1234-5678"
text2 = "他的电话是139-8765-4321"
# 使用编译后的模式
match1 = phone_pattern.search(text1)
match2 = phone_pattern.search(text2)
print(f"电话1: {match1.group()}") # 138-1234-5678
print(f"电话2: {match2.group()}") # 139-8765-4321
编译标志
import re
text = "Hello WORLD"
# 忽略大小写
pattern = re.compile(r"hello", re.IGNORECASE)
print(pattern.search(text).group()) # Hello
# 多行模式
multiline_text = """第一行
第二行
第三行"""
# 使用MULTILINE标志
pattern1 = re.compile(r"^第二行", re.MULTILINE)
print(pattern1.search(multiline_text).group()) # 第二行
# 点号匹配所有字符(包括换行符)
pattern2 = re.compile(r"第一行.*第三行", re.DOTALL)
print(pattern2.search(multiline_text).group()) # 第一行\n第二行\n第三行