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. 性能优化对大规模文本很重要

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值