一、什么是正则表达式?
正则表达式(Regular Expression,简称 Regex)是一种用于匹配字符串中字符组合的模式。它由普通字符和特殊字符(元字符)组成,可以用来验证、搜索、替换文本。
🎯 核心价值:用一行代码完成复杂的字符串处理任务
二、基础语法
2.1 普通字符
| 字符 | 说明 | 示例 |
|---|---|---|
abc |
匹配字符串 “abc” |
hello 匹配 hello
|
123 |
匹配数字 |
123abc 匹配 123
|
|
转义字符 |
. 匹配实际的点号 |
2.2 元字符(Metacharacters)
| 元字符 | 说明 | 示例 | 匹配结果 |
|---|---|---|---|
. |
匹配任意单个字符(除换行符) | h.t |
hat, hot, h t
|
^ |
匹配字符串开头 | ^Hello |
以 Hello 开头的字符串 |
$ |
匹配字符串结尾 | world$ |
以 world 结尾的字符串 |
| |
或运算符 | cat|dog |
cat 或 dog |
() |
分组捕获 | (ab)+ |
匹配一个或多个 ab |
[] |
字符集 | [abc] |
匹配 a、b 或 c |
2.3 量词(Quantifiers)
| 量词 | 说明 | 示例 | 匹配次数 |
|---|---|---|---|
* |
零次或多次 | ab*c |
ac, abc, abbc… |
+ |
一次或多次 | ab+c |
abc, abbc…(不匹配 ac) |
? |
零次或一次 | colou?r |
color, colour |
{n} |
恰好 n 次 | a{3} |
aaa |
{n,} |
至少 n 次 | a{2,} |
aa, aaa, aaaa… |
{n,m} |
n 到 m 次 | a{2,4} |
aa, aaa, aaaa |
2.4 字符类(Character Classes)
| 类 | 说明 | 示例 |
|---|---|---|
d |
数字 [0-9] |
d+ 匹配 123 |
D |
非数字 |
D+ 匹配 abc |
w |
单词字符 [a-zA-Z0-9_] |
w+ 匹配 hello_123 |
W |
非单词字符 |
W+ 匹配 @#$ |
s |
空白字符(空格、制表符、换行符) |
s+ 匹配多个空格 |
S |
非空白字符 |
S+ 匹配 hello |
b |
单词边界 |
bwordb 精确匹配 word |
B |
非单词边界 |
BwordB 匹配 swordfish 中的 word |
2.5 反义(Negations)
| 类 | 说明 | 示例 |
|---|---|---|
[^abc] |
不匹配 a、b、c |
[^0-9] 匹配非数字 |
[a-z] |
匹配小写字母 |
[a-z]+ 匹配 hello |
[A-Z] |
匹配大写字母 |
[A-Z]+ 匹配 HELLO |
[0-9] |
匹配数字 |
[0-9]+ 匹配 12345 |
三、进阶语法
3.1 贪婪与非贪婪
| 模式 | 说明 | 示例 |
|---|---|---|
.* |
贪婪匹配(尽可能多) |
.* in aabbcc 匹配 aabbcc
|
.*? |
非贪婪匹配(尽可能少) |
.*? in aabbcc 匹配空字符串 |
.+? |
非贪婪匹配 |
.+? in aabbcc 匹配 a
|
{n,m}? |
非贪婪量词 |
a{2,4}? 匹配 aa
|
示例:
import re html = 'Hello
World
' # 贪婪匹配(错误) re.findall(r'.*
', html) # ['Hello
World
'] # 非贪婪匹配(正确) re.findall(r'.*?
', html) # ['Hello
', 'World
']
3.2 前瞻与后顾(Lookaround)
| 类型 | 语法 | 说明 |
|---|---|---|
| 正向前瞻 | (?=pattern) |
匹配后面跟着 pattern 的位置 |
| 负向前瞻 | (?!pattern) |
匹配后面不跟着 pattern 的位置 |
| 正向后顾 | (? |
匹配前面是 pattern 的位置 |
| 负向后顾 | (? |
匹配前面不是 pattern 的位置 |
示例:
import re # 提取价格(不包含货币符号) text = '价格:$100 和 ¥200' # 正向前瞻 re.findall(r'(?
3.3 分组与捕获
import re
# 基本分组
pattern = r'(d{3})-(d{4})'
text = '电话:123-4567'
match = re.search(pattern, text)
if match:
print(match.group(0)) # 123-4567(完整匹配)
print(match.group(1)) # 123(第一组)
print(match.group(2)) # 4567(第二组)
print(match.groups()) # ('123', '4567')
3.4 命名分组
import re
# 命名分组 (?Ppattern)
pattern = r'(?Pd{4})-(?Pd{2})-(?Pd{2})'
text = '日期:2024-01-15'
match = re.search(pattern, text)
if match:
print(match.group('year')) # 2024
print(match.group('month')) # 01
print(match.group('day')) # 15
3.5 非捕获分组
import re
# 非捕获分组 (?:pattern) - 不保存匹配结果
pattern = r'(?:http|https)://(S+)'
text = '访问 https://example.com'
match = re.search(pattern, text)
if match:
print(match.group(0)) # https://example.com
print(match.group(1)) # example.com(只有捕获组)
四、Python re 模块详解
4.1 常用函数
| 函数 | 说明 | 返回值 |
|---|---|---|
re.match() |
从字符串开头匹配 | Match 对象或 None |
re.search() |
搜索整个字符串 | 第一个匹配的 Match 对象 |
re.findall() |
查找所有匹配 | 列表 |
re.finditer() |
查找所有匹配 | 迭代器 |
re.sub() |
替换匹配的字符串 | 新字符串 |
re.split() |
按模式分割字符串 | 列表 |
re.compile() |
编译正则表达式 | Pattern 对象 |
4.2 使用示例
import re
# 1. match - 从开头匹配
re.match(r'd+', '123abc') # Match: '123'
re.match(r'd+', 'abc123') # None(不是开头)
# 2. search - 搜索第一个匹配
re.search(r'd+', 'abc123def') # Match: '123'
# 3. findall - 查找所有匹配
re.findall(r'd+', 'a1b2c3') # ['1', '2', '3']
# 4. sub - 替换
re.sub(r'd+', '*', 'a1b2c3') # 'a*b*c*'
re.sub(r'(w+)', r'11', 'ab') # 'aabb'(重复单词)
# 5. split - 分割
re.split(r's+', 'hello world foo') # ['hello', 'world', 'foo']
re.split(r'[;|,]', 'a;b,c|d') # ['a', 'b', 'c', 'd']
# 6. compile - 编译复用
email_pattern = re.compile(r'[w.]+@[w.]+.w+')
email_pattern.findall('test@example.com and info@test.org')
# ['test@example.com', 'info@test.org']
4.3 编译标志(Flags)
| 标志 | 说明 | 使用方式 |
|---|---|---|
re.IGNORECASE |
忽略大小写 |
re.IGNORECASE 或 re.I
|
re.MULTILINE |
多行模式(^ $ 匹配每行) |
re.MULTILINE 或 re.M
|
re.DOTALL |
点号匹配换行符 |
re.DOTALL 或 re.S
|
re.VERBOSE |
允许注释和空格 |
re.VERBOSE 或 re.X
|
re.UNICODE |
Unicode 模式 |
re.UNICODE 或 re.U
|
import re
# 忽略大小写
re.findall(r'hello', 'Hello HELLO hello', re.I) # ['Hello', 'HELLO', 'hello']
# 多行模式
text = 'line1nline2nline3'
re.findall(r'^w+', text, re.M) # ['line1', 'line2', 'line3']
# 点号匹配换行符
re.findall(r'.+', 'line1nline2', re.S) # ['line1nline2']
# 详细的正则(忽略空格和注释)
phone = re.compile(r'''
(d{3,4}) # 区号
[-s]? # 可选分隔符
(d{7,8}) # 电话号码
''', re.VERBOSE)
五、爬虫实战常用正则
5.1 URL 提取
import re
# 匹配 HTTP/HTTPS URL
url_pattern = r'https?://(?:[-w.]|(?:%[da-fA-F]{2}))+[^s]*'
text = '请访问 https://example.com/path?query=1 或 http://test.org'
re.findall(url_pattern, text)
# ['https://example.com/path?query=1', 'http://test.org']
# 匹配相对 URL
relative_pattern = r'href=["']([^"']+)["']'
html = 'Link'
re.findall(relative_pattern, html) # ['/page1']
5.2 邮箱提取
import re
# 基本邮箱验证
email_pattern = r'[w.+-]+@[w-]+.[w.-]+'
# 更严格的邮箱验证
strict_email = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$'
emails = """
联系方式:
- 简单邮箱:test@example.com
- 公司邮箱:user.name@company.co.uk
- 个人邮箱:user+tag@gmail.com
"""
re.findall(email_pattern, emails)
# ['test@example.com', 'user.name@company.co.uk', 'user+tag@gmail.com']
5.3 手机号提取
import re
# 中国大陆手机号
phone_pattern = r'1[3-9]d{9}'
# 带格式的手机号
formatted_phone = r'1[3-9]d{1}[-s]?d{4}[-s]?d{4}'
text = '联系我:13812345678 或 139-1234-5678'
re.findall(phone_pattern, text) # ['13812345678', '13912345678']
5.4 IP 地址提取
import re
# 基本 IPv4
ip_pattern = r'b(?:d{1,3}.){3}d{1,3}b'
# 严格的 IPv4 验证
strict_ip = r'^(?:(?:25[0-5]|2[0-4]d|[01]?dd?).){3}(?:25[0-5]|2[0-4]d|[01]?dd?)$'
log = '访问来自 192.168.1.100 和 10.0.0.1'
re.findall(ip_pattern, log) # ['192.168.1.100', '10.0.0.1']
5.5 HTML 内容提取
import re # 提取标题 title_pattern = r'(.*?) ' # 提取 meta 描述 meta_pattern = r']+>' html = '''网页标题 主标题
段落内容
''' re.findall(title_pattern, html) # ['网页标题'] re.sub(text_pattern, '', html) # '网页标题主标题段落内容'
5.6 数据清洗
import re # 去除多余空白 clean = r's+' text = ' hello world n foo ' re.sub(clean, ' ', text.strip()) # 'hello world foo' # 去除 HTML 实体 entity_pattern = r'&[a-zA-Z]+;|d+;' text = '价格:100': '>', '&': '&' }.get(m.group(), ''), text) # 中文提取 chinese_pattern = r'[u4e00-u9fa5]+' text = 'Hello 你好 World 世界' re.findall(chinese_pattern, text) # ['你好', '世界']
六、常用正则表达式速查表
6.1 验证类
# 手机号验证
r'^1[3-9]d{9}$'
# 邮箱验证
r'^[w.+-]+@[w-]+.[w.-]+$'
# 身份证号验证(18位)
r'^[1-9]d{5}(19|20)d{2}(0[1-9]|1[0-2])(0[1-9]|[12]d|3[01])d{3}[dXx]$'
# URL 验证
r'^https?://[-w.]+(?:/[-w./]*)?$'
# IP 地址验证
r'^(?:(?:25[0-5]|2[0-4]d|[01]?dd?).){3}(?:25[0-5]|2[0-4]d|[01]?dd?)$'
6.2 提取类
# 中文字符
r'[u4e00-u9fa5]+'
# 英文单词
r'b[a-zA-Z]+b'
# 数字(整数和小数)
r'-?d+.?d*'
# 日期格式 YYYY-MM-DD
r'd{4}[-/]d{1,2}[-/]d{1,2}'
# 时间格式 HH:MM:SS
r'd{1,2}:d{2}:d{2}'
# Hex 颜色值
r'#(?:[0-9a-fA-F]{3}){1,2}b'
6.3 替换类
# 压缩多个空格 r's+' # HTML 标签 r']+>' # 中文标点转英文标点 r'[,。;:!?]' # 全角转半角 r'[uff01-uff5e]'
七、性能优化技巧
7.1 编译正则
import re
# ❌ 不推荐:每次调用都重新编译
def find_emails(text):
return re.findall(r'[w.+-]+@[w-]+.[w.-]+', text)
# ✅ 推荐:编译一次复用
EMAIL_PATTERN = re.compile(r'[w.+-]+@[w-]+.[w.-]+')
def find_emails(text):
return EMAIL_PATTERN.findall(text)
7.2 使用原始字符串
# ❌ 不推荐:需要双重转义 pattern = "\d+\.\d+" # ✅ 推荐:原始字符串 pattern = r'd+.d+'
7.3 避免回溯
import re # ❌ 容易产生回溯(灾难性回溯) bad_pattern = r'(a+)+b' # ✅ 更高效的写法 good_pattern = r'a+b'
7.4 使用非贪婪匹配
import re html = 'contentmore' # ❌ 贪婪匹配 re.findall(r'.*', html) # ['contentmore'] # ✅ 非贪婪匹配 re.findall(r'.*?', html) # ['content', 'more']
八、常见错误与调试
8.1 常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
re.error: nothing to repeat |
量词前没有字符 | 检查正则表达式 |
re.error: bad escape |
无效的转义序列 | 使用原始字符串 |
| 匹配结果为空 | 正则写错或模式不匹配 | 逐步调试正则 |
8.2 调试技巧
import re
# 使用 re.VERBOSE 添加注释
pattern = re.compile(r'''
^ # 字符串开头
(?Pd{4}) # 年
- # 分隔符
(?Pd{2}) # 月
- # 分隔符
(?Pd{2}) # 日
$ # 字符串结尾
''', re.VERBOSE)
# 在线调试工具
# https://regex101.com/
# https://debuggex.com/
九、总结
📋 正则表达式核心要点
-
基础元字符:
.,^,$,*,+,?,[],(),| -
字符类:
d,w,s及其大写反义 -
量词:
*,+,?,{n},{n,},{n,m} -
贪婪与非贪婪:
.*vs.*? -
分组捕获:
()和命名分组(?P) -
前瞻后顾:
(?=),(?!),(?,(?
🎯 学习建议
- 从简单模式开始,逐步复杂化
- 使用在线工具(regex101.com)测试
- 先理解需求,再编写正则
- 避免过度使用,可读性很重要
- 注意性能,避免灾难性回溯
到此这篇关于正则表达式从入门到精通(完全指南)的文章就介绍到这了,更多相关正则表达式入门内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!
