python 正则表达式
- 一、匹配函数
- 二、基础匹配
- 三、重复次数
-
- [3.1、* :0次或多次](#3.1、* :0次或多次)
- [3.2、+ :1次或多次](#3.2、+ :1次或多次)
- [3.3、? :0次或1次](#3.3、? :0次或1次)
- [3.4、{n} :精确n次](#3.4、{n} :精确n次)
- 3.5、{n,}:至少n次
- 3.6、{n,m}:n到m次
- 四、分组与引用
- 五、特殊字符
-
- [5.1、. :匹配任意字符(除换行符)](#5.1、. :匹配任意字符(除换行符))
- [5.2、^ :匹配字符串开头](#5.2、^ :匹配字符串开头)
- [5.3、 :匹配字符串结尾](#5.3、 :匹配字符串结尾)
- [5.4、\d :匹配数字](#5.4、\d :匹配数字)
- [5.5、\D :匹配非数字](#5.5、\D :匹配非数字)
- [5.6、\s :匹配空白字符](#5.6、\s :匹配空白字符)
- [5.7、\S :匹配非空白字符](#5.7、\S :匹配非空白字符)
- [5.8、\w :匹配单词字符(字母、数字、下划线)](#5.8、\w :匹配单词字符(字母、数字、下划线))
- [5.9、\W :匹配非单词字符](#5.9、\W :匹配非单词字符)
- 六、贪婪与非贪婪
- 七、替换与分割
-
- [7.1 re.sub() - 替换](#7.1 re.sub() - 替换)
- [7.2 re.subn() - 替换并返回替换次数](#7.2 re.subn() - 替换并返回替换次数)
- 八、正则编译
-
- [8.1 re.compile() - 预编译正则表达式](#8.1 re.compile() - 预编译正则表达式)
- 九、常用匹配模式
-
- [9.1、re.IGNORECASE (re.I) - 忽略大小写](#9.1、re.IGNORECASE (re.I) - 忽略大小写)
- [9.2 re.MULTILINE (re.M) - 多行模式](#9.2 re.MULTILINE (re.M) - 多行模式)
- [9.3 re.DOTALL (re.S) - 点号匹配所有字符(包括换行符)](#9.3 re.DOTALL (re.S) - 点号匹配所有字符(包括换行符))
- [9.4 re.VERBOSE (re.X) - 详细模式(允许添加注释和空白)](#9.4 re.VERBOSE (re.X) - 详细模式(允许添加注释和空白))
- [9.5 组合多个标志](#9.5 组合多个标志)
- 十、综合例子
一、匹配函数
1.1、re.match()
从字符串开头匹配模式,如果开头不匹配则返回None。
import re
# 示例1:匹配成功
result = re.match(r'hello', 'hello world')
print(result.group()) # 输出: hello
# 示例2:匹配失败(开头不匹配)
result = re.match(r'world', 'hello world')
print(result) # 输出: None
1.2、re.search()
扫描整个字符串查找第一个匹配项。
# 示例1:查找第一个数字
result = re.search(r'\d+', 'abc 123 def 456')
print(result.group()) # 输出: 123
# 示例2:查找单词
result = re.search(r'world', 'hello world')
print(result.group()) # 输出: world
1.3、re.findall()
返回所有匹配项的列表。
# 示例1:查找所有数字
result = re.findall(r'\d+', 'abc 123 def 456')
print(result) # 输出: ['123', '456']
# 示例2:查找所有单词
result = re.findall(r'\w+', 'hello world, python!')
print(result) # 输出: ['hello', 'world', 'python']
1.4、re.finditer()
返回一个迭代器,包含所有匹配的Match对象。
# 示例
matches = re.finditer(r'\d+', 'abc 123 def 456')
for match in matches:
print(match.group())
# 输出:
# 123
# 456
1.5、re.fullmatch()
整个字符串必须完全匹配模式。
# 示例1:完全匹配
result = re.fullmatch(r'hello world', 'hello world')
print(result.group()) # 输出: hello world
# 示例2:不完全匹配
result = re.fullmatch(r'hello', 'hello world')
print(result) # 输出: None
二、基础匹配
2.1、普通字符匹配
直接匹配字符本身。
# 示例
result = re.search(r'python', 'I love python programming')
print(result.group()) # 输出: python
2.2、特殊字符匹配
使用反斜杠转义特殊字符。
# 示例1:匹配点号
result = re.search(r'\.', 'example.com')
print(result.group()) # 输出: .
# 示例2:匹配美元符号
result = re.search(r'\$100', 'Price: $100')
print(result.group()) # 输出: $100
2.3、字符类
使用方括号定义字符集。
# 示例1:匹配元音字母
result = re.findall(r'[aeiou]', 'hello world')
print(result) # 输出: ['e', 'o', 'o']
# 示例2:匹配数字或字母
result = re.search(r'[0-9a-zA-Z]', '#abc123!')
print(result.group()) # 输出: a
三、重复次数
3.1、* :0次或多次
# 示例
result = re.findall(r'ab*c', 'ac abc abbc abbbc')
print(result) # 输出: ['ac', 'abc', 'abbc', 'abbbc']
3.2、+ :1次或多次
# 示例
result = re.findall(r'ab+c', 'ac abc abbc abbbc')
print(result) # 输出: ['abc', 'abbc', 'abbbc']
3.3、? :0次或1次
# 示例
result = re.findall(r'ab?c', 'ac abc abbc')
print(result) # 输出: ['ac', 'abc']
3.4、{n} :精确n次
# 示例
result = re.findall(r'ab{2}c', 'abc abbc abbbc')
print(result) # 输出: ['abbc']
3.5、{n,}:至少n次
# 示例
result = re.findall(r'ab{2,}c', 'abc abbc abbbc abbbbc')
print(result) # 输出: ['abbc', 'abbbc', 'abbbbc']
3.6、{n,m}:n到m次
# 示例
result = re.findall(r'ab{1,3}c', 'abc abbc abbbc abbbbc')
print(result) # 输出: ['abc', 'abbc', 'abbbc']
四、分组与引用
4.1、基本分组()
# 示例
result = re.search(r'(\d{3})-(\d{4})', 'Phone: 123-4567')
print(result.group()) # 输出: 123-4567
print(result.group(1)) # 输出: 123
print(result.group(2)) # 输出: 4567
4.2、非捕获分组(?:)
# 示例
result = re.search(r'(?:\d{3})-(\d{4})', 'Phone: 123-4567')
print(result.groups()) # 输出: ('4567',) 只有第二个分组被捕获
4.3、命名分组(?P)
# 示例
result = re.search(r'(?P<area>\d{3})-(?P<number>\d{4})', 'Phone: 123-4567')
print(result.groupdict()) # 输出: {'area': '123', 'number': '4567'}
4.4、反向引用
# 示例1:匹配重复单词
result = re.search(r'(\b\w+\b)\s+\1', 'hello hello world')
print(result.group()) # 输出: hello hello
# 示例2:命名分组的反向引用
result = re.search(r'(?P<word>\b\w+\b)\s+(?P=word)', 'hello hello world')
print(result.group()) # 输出: hello hello
五、特殊字符
5.1、. :匹配任意字符(除换行符)
# 示例
result = re.findall(r'a.b', 'aab abb a b acb a\nb')
print(result) # 输出: ['aab', 'abb', 'acb']
5.2、^ :匹配字符串开头
# 示例
result = re.findall(r'^hello', 'hello world\nhello python')
print(result) # 输出: ['hello']
5.3、$ :匹配字符串结尾
# 示例
result = re.findall(r'python$', 'hello world\nhello python')
print(result) # 输出: ['python']
5.4、\d :匹配数字
# 示例
result = re.findall(r'\d+', 'abc 123 def 456')
print(result) # 输出: ['123', '456']
5.5、\D :匹配非数字
# 示例
result = re.findall(r'\D+', 'abc 123 def 456')
print(result) # 输出: ['abc ', ' def ']
5.6、\s :匹配空白字符
# 示例
result = re.findall(r'\s+', 'hello\tworld\npython')
print(result) # 输出: ['\t', '\n']
5.7、\S :匹配非空白字符
# 示例
result = re.findall(r'\S+', 'hello\tworld\npython')
print(result) # 输出: ['hello', 'world', 'python']
5.8、\w :匹配单词字符(字母、数字、下划线)
# 示例
result = re.findall(r'\w+', 'hello_world 123! python')
print(result) # 输出: ['hello_world', '123', 'python']
5.9、\W :匹配非单词字符
# 示例
result = re.findall(r'\W+', 'hello_world 123! python')
print(result) # 输出: [' ', '! ']
六、贪婪与非贪婪
6.1、贪婪匹配(默认)
# 示例
result = re.search(r'<.*>', '<html><head><title>Title</title></head></html>')
print(result.group()) # 输出: <html><head><title>Title</title></head></html>
6.2 非贪婪匹配 ?
# 示例
result = re.search(r'<.*?>', '<html><head><title>Title</title></head></html>')
print(result.group()) # 输出: <html>
6.3 贪婪与非贪婪对比
# 贪婪匹配
"""
贪婪匹配 (Greedy Matching)
定义:正则表达式默认是贪婪模式,会尽可能多地匹配字符
表示方式:*, +, ?, {n,m} 等量词默认都是贪婪的
工作原理:匹配引擎会尽可能多地匹配字符,然后根据需要进行回溯
"""
result = re.findall(r'a.*b', 'aab abb a b acb')
print(result) # 输出: ['aab abb a b acb']
"""
解释:
< 匹配第一个 < 字符
.* 贪婪地匹配尽可能多的字符,直到字符串末尾
然后从字符串末尾回溯,寻找可以匹配 > 的位置
最终匹配到整个字符串的最后一个 >,因此匹配了整个字符串
"""
# 非贪婪匹配
"""
非贪婪匹配 (Lazy/Non-greedy Matching)
定义:在量词后加 ? 使其变为非贪婪模式,会尽可能少地匹配字符
表示方式:*?, +?, ??, {n,m}? 等
工作原理:匹配引擎会尽可能少地匹配字符,一旦满足条件就停止
"""
result = re.findall(r'a.*?b', 'aab abb a b acb')
print(result) # 输出: ['aab', 'abb', 'a b', 'acb']
"""
解释:
< 匹配第一个 < 字符
.*? 非贪婪地匹配尽可能少的字符
遇到第一个 > 就立即停止匹配
因此只匹配到第一个标签 <html>
"""
七、替换与分割
7.1 re.sub() - 替换
# 示例1:简单替换
result = re.sub(r'\d+', 'NUM', 'abc 123 def 456')
print(result) # 输出: abc NUM def NUM
# 示例2:使用函数处理匹配项
def double(match):
return str(int(match.group()) * 2)
result = re.sub(r'\d+', double, 'abc 123 def 456')
print(result) # 输出: abc 246 def 912
7.2 re.subn() - 替换并返回替换次数
# 示例
result, count = re.subn(r'\d+', 'NUM', 'abc 123 def 456')
print(result) # 输出: abc NUM def NUM
print(count) # 输出: 2
7.3 re.split() - 分割字符串
# 示例1:简单分割
result = re.split(r'\s+', 'hello world\tpython')
print(result) # 输出: ['hello', 'world', 'python']
# 示例2:保留分隔符
result = re.split(r'(\s+)', 'hello world\tpython')
print(result) # 输出: ['hello', ' ', 'world', '\t', 'python']
八、正则编译
8.1 re.compile() - 预编译正则表达式
# 示例
pattern = re.compile(r'\d{3}-\d{4}')
# 多次使用编译后的模式
result1 = pattern.search('Phone: 123-4567')
result2 = pattern.search('Another: 789-0123')
print(result1.group()) # 输出: 123-4567
print(result2.group()) # 输出: 789-0123
九、常用匹配模式
9.1、re.IGNORECASE (re.I) - 忽略大小写
# 示例
result = re.findall(r'python', 'Python PYTHON python', flags=re.IGNORECASE)
print(result) # 输出: ['Python', 'PYTHON', 'python']
9.2 re.MULTILINE (re.M) - 多行模式
# 示例
text = """first line
second line
third line"""
# 不使用多行模式
result = re.findall(r'^\w+', text)
print(result) # 输出: ['first']
# 使用多行模式
result = re.findall(r'^\w+', text, flags=re.MULTILINE)
print(result) # 输出: ['first', 'second', 'third']
9.3 re.DOTALL (re.S) - 点号匹配所有字符(包括换行符)
# 示例
text = """start
end"""
# 不使用DOTALL
result = re.search(r'start.*end', text)
print(result) # 输出: None
# 使用DOTALL
result = re.search(r'start.*end', text, flags=re.DOTALL)
print(result.group()) # 输出: start\nend
9.4 re.VERBOSE (re.X) - 详细模式(允许添加注释和空白)
# 示例
pattern = re.compile(r"""
\d{3} # 区号
- # 分隔符
\d{4} # 号码
""", flags=re.VERBOSE)
result = pattern.search('Phone: 123-4567')
print(result.group()) # 输出: 123-4567
9.5 组合多个标志
# 示例
text = """First line
second line
THIRD line"""
# 组合IGNORECASE和MULTILINE
result = re.findall(r'^[a-z]+\s', text, flags=re.I | re.M)
print(result) # 输出: ['First ', 'second ', 'THIRD ']
十、综合例子
10.1、提取和验证电子邮件地址
import re
# 电子邮件正则表达式
email_pattern = r"""
^ # 字符串开始
[a-zA-Z0-9._%+-]+ # 用户名部分(字母、数字、点、下划线等)
@ # @符号
[a-zA-Z0-9.-]+ # 域名部分
\. # 点号
[a-zA-Z]{2,} # 顶级域名(至少2个字母)
$ # 字符串结束
"""
# 编译正则表达式,使用VERBOSE模式允许注释和空白
email_regex = re.compile(email_pattern, flags=re.VERBOSE)
# 测试数据
emails = [
"user@example.com",
"first.last@sub.domain.co.uk",
"invalid.email@",
"no_at_symbol.com",
"user@123.123.123.123",
"user+tag@example.org"
]
# 验证和提取
print("电子邮件验证结果:")
for email in emails:
if email_regex.fullmatch(email):
print(f"✅ 有效: {email}")
# 提取用户名和域名
match = email_regex.match(email)
username = email.split('@')[0]
domain = email.split('@')[1]
print(f" 用户名: {username}, 域名: {domain}")
else:
print(f"❌ 无效: {email}")
# 从文本中提取电子邮件
text = "联系我: john.doe@example.com 或 support@company.co.uk 获取帮助"
found_emails = email_regex.findall(text)
print("\n从文本中提取的电子邮件:", found_emails)
10.2、示例2:解析和提取URL信息
import re
# URL解析正则表达式
url_pattern = r"""
^(https?://) # 协议 (http:// 或 https://)
([\w.-]+) # 域名
(:\d+)? # 端口 (可选)
(/[\w./?-]*)? # 路径 (可选)
(\?[\w&=]*)? # 查询字符串 (可选)
(#[\w-]*)? # 片段标识 (可选)
$
"""
url_regex = re.compile(url_pattern, flags=re.VERBOSE)
# 测试URLs
urls = [
"https://www.example.com",
"http://sub.domain.co.uk:8080/path/to/page?query=string#section",
"ftp://invalid.protocol.com",
"www.missing.protocol.com",
"https://api.service.com/v1/users?id=123&sort=desc"
]
print("\nURL解析结果:")
for url in urls:
match = url_regex.match(url)
if match:
print(f"✅ 有效URL: {url}")
print(" 协议:", match.group(1))
print(" 域名:", match.group(2))
print(" 端口:", match.group(3) if match.group(3) else "默认")
print(" 路径:", match.group(4) if match.group(4) else "/")
print(" 查询:", match.group(5) if match.group(5) else "无")
print(" 片段:", match.group(6) if match.group(6) else "无")
else:
print(f"❌ 无效URL: {url}")
# 从HTML中提取所有链接
html_text = """
<html>
<a href="https://main.site.com">首页</a>
<a href="/about">关于我们</a>
<img src="https://cdn.site.com/images/logo.png">
<script src="/static/js/app.js"></script>
"""
link_pattern = r'(?:href|src)="([^"]*)"'
links = re.findall(link_pattern, html_text)
print("\n从HTML提取的链接:", links)
10.3、示例3:日志文件分析
import re
from collections import defaultdict
# 日志格式示例: 127.0.0.1 - - [10/Oct/2023:13:55:36 +0800] "GET /api/user HTTP/1.1" 200 1234
log_pattern = r"""
^(\S+) # IP地址
\s+\S+\s+\S+ # 远程登录名和用户
\s+\[([^]]+)\] # 时间戳
\s+"(\S+)\s+(\S+)\s+(\S+)" # 请求方法、路径和协议
\s+(\d+) # 状态码
\s+(\d+) # 响应大小
"""
log_regex = re.compile(log_pattern, flags=re.VERBOSE)
# 示例日志数据
log_entries = [
'127.0.0.1 - - [10/Oct/2023:13:55:36 +0800] "GET /api/user HTTP/1.1" 200 1234',
'192.168.1.1 - - [10/Oct/2023:13:56:12 +0800] "POST /api/login HTTP/1.1" 201 567',
'10.0.0.1 - - [10/Oct/2023:13:57:01 +0800] "GET /api/products HTTP/1.1" 200 8901',
'127.0.0.1 - - [10/Oct/2023:13:58:22 +0800] "GET /api/user HTTP/1.1" 200 1234',
'192.168.1.2 - - [10/Oct/2023:13:59:45 +0800] "GET /api/products HTTP/1.1" 404 0'
]
# 分析日志
ip_counts = defaultdict(int)
endpoint_counts = defaultdict(int)
status_counts = defaultdict(int)
print("\n日志分析结果:")
for entry in log_entries:
match = log_regex.match(entry)
if match:
ip = match.group(1)
timestamp = match.group(2)
method = match.group(3)
path = match.group(4)
protocol = match.group(5)
status = match.group(6)
size = match.group(7)
ip_counts[ip] += 1
endpoint_counts[path] += 1
status_counts[status] += 1
print(f"IP: {ip}, 时间: {timestamp}, 方法: {method}, 路径: {path}, 状态码: {status}")
print("\n统计信息:")
print("IP访问次数:", dict(ip_counts))
print("端点访问次数:", dict(endpoint_counts))
print("状态码统计:", dict(status_counts))
10.4、示例4:数据清洗和格式化
import re
# 原始数据
raw_data = """
姓名: 张三, 电话: 138-1234-5678, 邮箱: zhangsan@example.com
姓名: 李四, 电话: (86)139-8765-4321, 邮箱: lisi123@gmail.com
姓名: 王五, 电话: 0086-189-12345678, 邮箱: wang.wu@company.org
姓名: 赵六, 电话: 12345678901, 邮箱: invalid.email
"""
# 提取信息的正则表达式
info_pattern = r"""
姓名:\s*([^,]+),\s* # 姓名
电话:\s*([^,]+),\s* # 电话
邮箱:\s*([^\n]+) # 邮箱
"""
# 电话号码标准化正则表达式
phone_pattern = r"""
(?:\(?(\+?\d{2,3})\)?[-. ]? # 国际区号
(\d{3})[-. ]? # 前3位
(\d{4})[-. ]? # 中间4位
(\d{4}) # 最后4位
"""
info_regex = re.compile(info_pattern, flags=re.VERBOSE)
phone_regex = re.compile(phone_pattern, flags=re.VERBOSE)
# 处理数据
print("数据清洗和格式化结果:")
for match in info_regex.finditer(raw_data):
name = match.group(1).strip()
raw_phone = match.group(2).strip()
email = match.group(3).strip()
# 标准化电话号码
phone_match = phone_regex.search(raw_phone)
if phone_match:
country_code = phone_match.group(1) or '86'
standardized_phone = f"+{country_code} {phone_match.group(2)} {phone_match.group(3)}-{phone_match.group(4)}"
else:
standardized_phone = "无效电话号码"
# 验证邮箱
if not re.match(r'^[\w.-]+@[\w.-]+\.[a-zA-Z]{2,}$', email):
email = f"无效邮箱: {email}"
print(f"姓名: {name}")
print(f"原始电话: {raw_phone}")
print(f"标准电话: {standardized_phone}")
print(f"邮箱: {email}")
print("-" * 30)
10.5、示例5:高级文本处理(Markdown转换为HTML)
import re
# Markdown文本
markdown_text = """
# 标题1
这是一个段落,包含**加粗**和*斜体*文本。
## 标题2
- 列表项1
- 列表项2
- 列表项3
[示例链接](https://example.com)
"""
# 转换规则
rules = [
(r'^#\s+(.+)$', r'<h1>\1</h1>'), # 标题1
(r'^##\s+(.+)$', r'<h2>\1</h2>'), # 标题2
(r'\*\*(.+?)\*\*', r'<strong>\1</strong>'), # 加粗
(r'\*(.+?)\*', r'<em>\1</em>'), # 斜体
(r'^-\s+(.+)$', r'<li>\1</li>'), # 列表项
(r'\[(.+?)\]\((.+?)\)', r'<a href="\2">\1</a>'), # 链接
(r'^\s*$', r'<br>') # 空行
]
# 编译所有正则表达式
compiled_rules = [(re.compile(pattern, flags=re.MULTILINE), replacement)
for pattern, replacement in rules]
# 转换函数
def markdown_to_html(text):
for pattern, replacement in compiled_rules:
text = pattern.sub(replacement, text)
# 处理列表
text = re.sub(r'(<li>.+</li>)+', r'<ul>\g<0></ul>', text, flags=re.DOTALL)
# 处理段落
paragraphs = []
for line in text.split('<br>'):
line = line.strip()
if line and not re.match(r'^<(h\d|ul|li)', line):
line = f'<p>{line}</p>'
paragraphs.append(line)
return '\n'.join(filter(None, paragraphs))
# 执行转换
html_output = markdown_to_html(markdown_text)
print("Markdown转换为HTML结果:")
print(html_output)
10.6、示例6:密码强度验证
import re
def validate_password(password):
"""
验证密码强度:
- 至少8个字符
- 包含大写和小写字母
- 包含数字
- 包含特殊字符
"""
if len(password) < 8:
return False, "密码太短,至少需要8个字符"
checks = [
(r'[A-Z]', "需要至少一个大写字母"),
(r'[a-z]', "需要至少一个小写字母"),
(r'[0-9]', "需要至少一个数字"),
(r'[^A-Za-z0-9]', "需要至少一个特殊字符")
]
failed = []
for pattern, message in checks:
if not re.search(pattern, password):
failed.append(message)
if failed:
return False, ",".join(failed)
else:
return True, "密码强度足够"
# 测试密码
passwords = [
"weak",
"Weak1",
"Weak1!",
"StrongPassword1!",
"12345678",
"ABCDEFGH",
"Abc123!@#"
]
print("\n密码强度验证结果:")
for pwd in passwords:
is_valid, message = validate_password(pwd)
status = "✅" if is_valid else "❌"
print(f"{status} {pwd}: {message}")