Python正则表达式

正则表达式是文本处理的强大工具,本文将系统全面地介绍正则表达式的所有知识点,结合Python的re模块,帮助读者从零开始掌握正则表达式的使用。

1. 正则表达式基础概念

1.1 什么是正则表达式?

正则表达式(Regular Expression,简称regex或RE)是一种用于描述字符串匹配规则的表达式,它并不是Python特有的,而是计算机科学中的一个通用概念。

核心功能

  • 验证:检查字符串是否符合特定格式(如邮箱、电话号码)

  • 提取:从文本中提取符合规则的内容

  • 替换:替换文本中的特定内容

1.2 为什么需要正则表达式?

假设我们需要:

  • 验证用户输入的邮箱是否合法

  • 从网页源码中提取所有URL

  • 将文本中的所有日期格式统一

使用普通字符串方法实现这些功能会非常繁琐,而正则表达式可以简洁高效地完成这些任务。

2. 正则表达式基本语法

2.1 字符匹配

2.1.1 普通字符

大多数字符(字母、数字)会直接匹配它们自身:

复制代码
import re
re.findall("abc", "abcde")  # 匹配['abc']
2.1.2 元字符

具有特殊含义的字符:

元字符 说明 示例
. 匹配除换行符外的任意字符 a.c → abc, a1c
\d 匹配数字,等价于[0-9] \d\d → 12
\D 匹配非数字 \D\D → ab
\w 匹配字母、数字、下划线 \w\w → a1, _a
\W 匹配非字母数字下划线 \W\W → @#
\s 匹配空白字符(空格、制表符等) a\sb → a b
\S 匹配非空白字符 \S\S → ab

2.2 字符组

[]表示,匹配其中任意一个字符:

复制代码
[aeiou]    # 匹配任意一个元音字母
[0-9]      # 匹配任意数字,等价于\d
[a-zA-Z]   # 匹配任意字母
[^0-9]     # 匹配非数字,等价于\D

2.3 量词

控制前面元素的匹配次数:

量词 说明 示例
* 0次或多次 a* → "", a, aa
+ 1次或多次 a+ → a, aa
? 0次或1次 a? → "", a
{n} 恰好n次 a{2} → aa
{n,} 至少n次 a{2,} → aa, aaa
{n,m} n到m次 a{2,3} → aa, aaa

2.4 边界匹配

边界符 说明 示例
^ 匹配字符串开始 ^abc → abc开头
$ 匹配字符串结束 abc$ → abc结尾
\b 匹配单词边界 \bfoo\b → 匹配"foo"单词
\B 匹配非单词边界 \Bfoo\B → 匹配"xfooy"中的foo

2.5 分组与或操作

分组 ()

将多个元素组合为一个整体:

复制代码
(abc)+  # 匹配abc, abcabc等
或操作 |

匹配左边或右边的表达式:

复制代码
a|b     # 匹配a或b
(ab)|(cd) # 匹配ab或cd

3. 正则表达式进阶技巧

3.1 贪婪与非贪婪匹配

  • 贪婪匹配(默认):尽可能匹配更长的字符串

  • 非贪婪匹配 :在量词后加?,尽可能匹配更短的字符串

    贪婪匹配

    re.findall("a.*b", "axbxb") # 匹配['axbxb']

    非贪婪匹配

    re.findall("a.*?b", "axbxb") # 匹配['axb', 'xb']

3.2 后向引用

使用\数字引用前面的分组:

复制代码
# 匹配重复单词
re.findall(r"\b(\w+)\b\s+\1\b", "hello hello world")  # 匹配['hello']

3.3 零宽断言

断言 说明 示例
(?=exp) 匹配后面是exp的位置 \d+(?=元) → 匹配"100元"中的100
(?!exp) 匹配后面不是exp的位置 \d+(?!元) → 匹配"100刀"中的100
(?<=exp) 匹配前面是exp的位置 (?<=)\\d+ → 匹配"100"中的100
(?<!exp) 匹配前面不是exp的位置 (?<!$)\d+ → 匹配"¥100"中的100

4. Python re模块详解

4.1 常用方法

re.match()

从字符串开头匹配,返回匹配对象或None:

复制代码
result = re.match(r"\d+", "123abc")
if result:
    print(result.group())  # 输出: 123
re.search()

扫描整个字符串,返回第一个匹配对象:

复制代码
result = re.search(r"\d+", "abc123def456")
print(result.group())  # 输出: 123
re.findall()

返回所有匹配的字符串列表

复制代码
re.findall(r"\d+", "123abc456def")  # 输出: ['123', '456']
re.finditer()

返回所有匹配的迭代器(适合大文本):

复制代码
for match in re.finditer(r"\d+", "123abc456def"):
    print(match.group())
# 输出:
# 123
# 456
re.sub()

替换匹配的字符串:

复制代码
re.sub(r"\d+", "NUM", "123abc456")  # 输出: 'NUMabcNUM'
re.split()

按匹配模式分割字符串:

复制代码
re.split(r"\d+", "abc123def456ghi")  # 输出: ['abc', 'def', 'ghi']

4.2 匹配对象方法

匹配成功后返回的匹配对象常用方法:

方法 说明 示例
group() 返回匹配的字符串 match.group() → "123"
start() 返回匹配的开始位置 match.start() → 0
end() 返回匹配的结束位置 match.end() → 3
span() 返回(start, end)元组 match.span() → (0, 3)
groups() 返回所有分组的元组 match.groups() → ('123',)

4.3 编译正则表达式

对于重复使用的正则表达式,可以先编译提高效率:

复制代码
pattern = re.compile(r"\d+")
pattern.findall("123abc456")  # 输出: ['123', '456']

5. 常用正则表达式示例

5.1 数字相关

复制代码
# 整数
^[+-]?\d+$

# 正整数
^[1-9]\d*$

# 负整数
^-[1-9]\d*$

# 非负整数
^\d+$

# 浮点数
^[+-]?\d+\.\d+$

# 保留两位小数
^\d+(\.\d{2})?$

5.2 字符串相关

复制代码
# 中文字符
^[\u4e00-\u9fa5]+$

# 英文数字
^[A-Za-z0-9]+$

# 用户名(字母开头,5-16字符)
^[a-zA-Z][a-zA-Z0-9_]{4,15}$

# 强密码(至少8位,含大小写字母和数字)
^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$

5.3 常用信息验证

复制代码
# 邮箱
^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

# 手机号(中国)
^(13[0-9]|14[5-9]|15[0-3,5-9]|16[2,5-7]|17[0-8]|18[0-9]|19[0-3,5-9])\d{8}$

# 身份证号(15或18位)
^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$

# IPv4地址
^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$

# URL
^https?://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$

6. 实际应用案例

6.1 提取网页中的所有链接

复制代码
import re
import requests

html = requests.get("https://example.com").text
links = re.findall(r'href=["\'](https?://.*?)["\']', html)
print(links)

6.2 验证用户输入

复制代码
def validate_email(email):
    pattern = r'^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$'
    return re.match(pattern, email) is not None

print(validate_email("test@example.com"))  # True
print(validate_email("invalid.email"))     # False

6.3 日志分析

复制代码
log = """
2023-01-01 12:00:00 [INFO] User login
2023-01-01 12:05:23 [ERROR] Connection timeout
2023-01-01 12:10:45 [WARNING] Disk space low
"""

# 提取所有ERROR级别的日志
errors = re.findall(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \[ERROR\] .*', log)
print(errors)

6.4 数据清洗

复制代码
text = "价格:¥100.50 特价:$99.99 优惠:50%"
cleaned = re.sub(r'[^\d.]', '', text)  # 只保留数字和小数点
print(cleaned)  # 输出: 100.5099.9950

7. 性能优化与最佳实践

7.1 编译正则表达式

对于频繁使用的正则表达式,预编译可提高性能:

复制代码
pattern = re.compile(r'\d+')  # 编译一次
for text in large_text_collection:
    pattern.findall(text)     # 多次使用

7.2 避免过度使用正则

简单字符串操作能解决的,不要用正则:

复制代码
# 不好
re.sub(r'\.$', '', text)

# 更好
text.rstrip('.')

7.3 使用非贪婪模式减少回溯

复制代码
# 贪婪模式(可能性能差)
re.match(r'<.*>', '<tag>content</tag>')

# 非贪婪模式(性能更好)
re.match(r'<.*?>', '<tag>content</tag>')

7.4 合理使用分组

只对需要的内容使用分组,避免不必要的捕获:

复制代码
# 不好(使用捕获分组)
re.findall(r'(http://\S+)', text)

# 更好(使用非捕获分组)
re.findall(r'(?:http://)\S+', text)

8. 常见问题与解决方案

Q1: 正则表达式为什么匹配不到?

可能原因:

  • 忘记转义特殊字符(如.*等)

  • 大小写不匹配(可添加re.IGNORECASE标志)

  • 多行模式下^$的行为不同(re.MULTILINE

Q2: 如何匹配多行文本?

使用re.DOTALL标志使.匹配换行符:

复制代码
re.findall(r'start.*end', text, re.DOTALL)

Q3: 正则表达式性能很差怎么办?

  • 避免嵌套量词(如(a+)+

  • 使用更具体的字符类(如\d代替.

  • 考虑使用非贪婪模式

  • 预编译正则表达式

Q4: 如何调试复杂的正则表达式?

  • 使用在线工具(如regex101.com

  • 分步测试各个部分

  • 添加注释(使用re.VERBOSE标志)

9. 总结

正则表达式是文本处理的强大工具,掌握它可以:

  • 高效验证数据格式

  • 快速提取所需信息

  • 灵活替换文本内容

  • 简化复杂的字符串操作

关键点回顾:

  1. 元字符和量词是基础

  2. 分组和或操作实现复杂匹配

  3. 贪婪与非贪婪模式影响匹配结果

  4. Python的re模块提供丰富方法

  5. 常用正则表达式可以复用

  6. 性能优化对大规模文本很重要

建议多练习实际案例,逐步掌握正则表达式的强大功能。遇到复杂问题时,可以拆分为多个简单正则表达式逐步解决。

相关推荐
小牛不爱吃糖5 分钟前
基于bert-lstm对微博评论的情感分析系统设计与实现
python·机器学习·bert·lstm
阿虎儿34 分钟前
Python List 详解
python
凤凰AI1 小时前
Python知识点4-嵌套循环&break和continue使用&死循环
开发语言·前端·python
Dxy12393102161 小时前
Python适配器模式详解:让不兼容的接口协同工作
开发语言·python·适配器模式
mortimer1 小时前
音视频字幕同步 之 从“理想模型”到“工程现实”的进化之路
python·ffmpeg·音视频开发
过往入尘土3 小时前
PyCharm高效入门指南
ide·python·pycharm
站大爷IP3 小时前
Python数字处理:从基础到进阶的实用指南
python
秋秋棠3 小时前
MyBatis Plus高效开发指南
开发语言·python·mybatis
pk_xz1234563 小时前
基于机器视觉的迈克耳孙干涉环自动计数系统设计与实现
网络·python·深度学习·数据挖掘·机器人
Reggie_L3 小时前
JVM-Java
java·jvm·python