pin_drop当前位置:知识文库 ❯ 图文
正则表达式基础语法详解 - Python re模块入门指南
一、什么是正则表达式
正则表达式(Regular Expression,简称 Regex 或 RE)是一种用于匹配、查找、替换文本中特定模式的强大工具。它使用一组特殊字符来定义搜索模式,能够高效地完成字符串验证、数据提取、文本替换等任务。
在Python中,我们可以使用内置的 re 模块来处理正则表达式。正则表达式广泛应用于表单验证、日志分析、数据清洗、爬虫提取等场景。
二、元字符详解
元字符是正则表达式中具有特殊含义的字符,它们是构建正则表达式的核心元素。以下是常用的元字符及其含义:
代码示例
import re
# . 匹配任意字符
print(re.findall('a.c', 'abc a1c a c aXc')) # ['abc', 'a1c', 'a c', 'aXc']
# ^ 匹配开头
print(re.findall('^Hello', 'Hello world')) # ['Hello']
print(re.findall('^Hello', 'Say Hello')) # []
# $ 匹配结尾
print(re.findall('world$', 'Hello world')) # ['world']
print(re.findall('world$', 'world Hello')) # []
# * + ? 量词
print(re.findall('ab*', 'a ab abb abbb')) # ['a', 'ab', 'abb', 'abbb']
print(re.findall('ab+', 'a ab abb abbb')) # ['ab', 'abb', 'abbb']
print(re.findall('ab?', 'a ab abb')) # ['a', 'ab', 'ab']
# | 逻辑或
print(re.findall('cat|dog', 'I have a cat and a dog')) # ['cat', 'dog']
三、字符类
字符类(Character Classes)用于匹配一组字符中的任意一个。使用方括号 [] 来定义字符类。
常用字符类
代码示例
import re
# \d 匹配数字
print(re.findall(r'\d+', '电话号码: 138-1234-5678')) # ['138', '1234', '5678']
# \w 匹配单词字符
print(re.findall(r'\w+', 'hello world! 你好')) # ['hello', 'world', '你好']
# \s 匹配空白字符
print(re.findall(r'\s+', 'hello world')) # [' ']
# 自定义字符类
print(re.findall(r'[aeiou]', 'hello world')) # ['e', 'o', 'o']
print(re.findall(r'[0-9a-f]', 'abc123xyz')) # ['a', 'b', 'c', '1', '2', '3']
# 取反字符类
print(re.findall(r'[^0-9]', 'abc123')) # ['a', 'b', 'c']
四、量词
量词用于指定前面的字符或字符类需要匹配的次数。量词默认是"贪婪"的,会尽可能多地匹配字符。
代码示例
import re
# {n} 精确匹配n次
print(re.findall(r'\d{4}', '年份: 2024, 月份: 06')) # ['2024']
# {n,} 至少匹配n次
print(re.findall(r'\d{2,}', '1 22 333 4444')) # ['22', '333', '4444']
# {n,m} 匹配n到m次
print(re.findall(r'\d{2,3}', '1 22 333 4444')) # ['22', '333', '44', '44']
# 贪婪 vs 非贪婪
text = '<div>hello</div><div>world</div>'
print(re.findall(r'<.*>', text)) # 贪婪: ['<div>hello</div><div>world</div>']
print(re.findall(r'<.*?>', text)) # 非贪婪: ['<div>', '</div>', '<div>', '</div>']
小贴士
贪婪与非贪婪模式:量词默认是贪婪的(尽可能多匹配)。在量词后面加上 ? 可以开启非贪婪模式(尽可能少匹配)。例如 .*? 是非贪婪匹配,在HTML解析等场景中非常有用。
五、锚点
锚点用于匹配位置而不是字符,它们不消耗任何字符,只是断言某个位置的存在。
代码示例
import re
# \b 单词边界
print(re.findall(r'\bcat\b', 'cat scatter category cat')) # ['cat', 'cat']
print(re.findall(r'\b\w{4}\b', 'I love Python and Java')) # ['love', 'Java']
# \B 非单词边界
print(re.findall(r'\Bcat\B', 'cat scatter category')) # ['cat'](仅在scatter中)
# ^ 和 $ 配合使用做精确匹配
print(re.findall(r'^\d{6}$', '123456')) # ['123456']
print(re.findall(r'^\d{6}$', '12345')) # []
print(re.findall(r'^\d{6}$', '1234567')) # []
六、转义字符
当需要匹配正则表达式中的特殊字符本身时,需要使用反斜杠 \ 进行转义。Python中推荐使用原始字符串 r'' 来避免双重转义问题。
代码示例
import re
# 匹配特殊字符本身
print(re.findall(r'\.', 'hello.world.test')) # ['.', '.', '.']
print(re.findall(r'\+', '1+2=3')) # ['+']
print(re.findall(r'\[', '[hello] [world]')) # ['[', '[']
print(re.findall(r'\(', 'func(arg)')) # ['(']
# 推荐使用原始字符串 r''
# 不使用 r'' 需要写成 \\\\
print(re.findall('\\.', 'hello.world')) # 可以但不推荐
print(re.findall(r'\.', 'hello.world')) # 推荐写法
最佳实践:在Python中编写正则表达式时,始终使用原始字符串
r'pattern'。这样可以避免Python字符串转义和正则转义之间的冲突,使代码更加清晰易读。
七、分组与捕获
使用圆括号 () 可以对正则表达式进行分组,实现捕获子匹配、提取特定部分等功能。
代码示例
import re
# 捕获分组
text = '2024-06-07'
pattern = r'(\d{4})-(\d{2})-(\d{2})'
match = re.search(pattern, text)
if match:
print(f'完整匹配: {match.group(0)}') # 2024-06-07
print(f'年份: {match.group(1)}') # 2024
print(f'月份: {match.group(2)}') # 06
print(f'日期: {match.group(3)}') # 07
# findall 返回分组内容
dates = '2024-01-15, 2024-06-07, 2025-12-25'
print(re.findall(r'(\d{4})-(\d{2})-(\d{2})', dates))
# [('2024', '01', '15'), ('2024', '06', '07'), ('2025', '12', '25')]
# 命名分组
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
match = re.search(pattern, '2024-06-07')
print(match.group('year')) # 2024
print(match.group('month')) # 06
# 非捕获分组
print(re.findall(r'(?:\d{3})-(\d{4})', '010-1234 021-5678')) # ['1234', '5678']
八、完整代码示例
下面通过几个实际场景来综合应用正则表达式基础语法:
代码示例
import re
# ========== 示例1: 验证邮箱地址 ==========
def validate_email(email):
"""验证邮箱格式是否正确"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
print(validate_email('user@example.com')) # True
print(validate_email('invalid-email')) # False
print(validate_email('test@domain.org')) # True
# ========== 示例2: 提取URL中的域名 ==========
def extract_domain(url):
"""从URL中提取域名"""
pattern = r'https?://(?:www\.)?([^/]+)'
match = re.search(pattern, url)
return match.group(1) if match else None
print(extract_domain('https://www.example.com/page')) # example.com
print(extract_domain('http://blog.test.org/post')) # blog.test.org
# ========== 示例3: 提取文本中的所有电话号码 ==========
def extract_phones(text):
"""提取各种格式的电话号码"""
# 匹配: 138-1234-5678, 13812345678, (010)12345678
pattern = r'1[3-9]\d{9}|(?:\d{3,4}-)?\d{7,8}'
return re.findall(pattern, text)
text = '联系电话: 13812345678, 010-12345678, 02112345678'
print(extract_phones(text)) # ['13812345678', '010-12345678', '02112345678']
# ========== 示例4: 替换文本中的敏感信息 ==========
def mask_info(text):
"""隐藏手机号中间4位"""
return re.sub(r'(1[3-9]\d)\d{4}(\d{4})', r'\1****\2', text)
print(mask_info('张三电话: 13812345678')) # 张三电话: 138****5678
# ========== 示例5: 分割复杂字符串 ==========
text = 'apple, banana; orange|grape, melon; pineapple'
fruits = re.split(r'[,;|]\s*', text)
print(fruits) # ['apple', 'banana', 'orange', 'grape', 'melon', 'pineapple']
九、注意事项
⚠️ 注意1:始终使用原始字符串
r''来定义正则表达式,避免Python字符串转义与正则转义产生冲突。
⚠️ 注意2:注意贪婪与非贪婪的区别。量词默认是贪婪的,会尽可能多地匹配。在解析HTML、提取标签内容等场景中,务必使用非贪婪模式
.*?,否则会匹配到最远的结束位置。
⚠️ 注意3:正则表达式性能问题。复杂的正则表达式可能导致性能下降甚至"回溯灾难"。对于简单字符串操作,优先使用Python内置的字符串方法(如
str.find()、str.replace()),它们通常比正则更快。
十、小结
-
元字符:. ^ $ * + ? | 是构建正则表达式的核心元素,理解它们的含义是使用正则的基础
-
字符类:\d \w \s 等预定义字符类和自定义字符类 [abc] 可以精确控制匹配范围
-
量词:{n} {n,} {n,m} 控制匹配次数,注意贪婪与非贪婪模式的区别
-
锚点:^ $ \b \B 用于匹配位置而非字符,适合做精确验证
-
分组:使用 () 进行分组捕获,配合命名分组 (?P
) 可以更清晰地提取数据
十一、练习题
练习1
编写一个函数 is_valid_password(password),验证密码是否满足以下条件:至少8位、包含大写字母、小写字母、数字和特殊字符(!@#$%^&*)。
常见问题
为什么在Python中推荐使用 r'' 原始字符串写正则?
因为Python字符串本身会处理反斜杠转义,如 \n 表示换行。如果不使用原始字符串,写 \d 会被Python先处理,可能导致意外结果。使用 r'\d' 可以确保反斜杠原样传递给正则引擎,避免双重转义问题。
正则表达式中的 . 能匹配换行符吗?
默认情况下,. 不匹配换行符。如果需要匹配包含换行的内容,可以使用 re.DOTALL(或 re.S)标志,例如 re.findall(r'.+', text, re.DOTALL),这样 . 就可以匹配包括换行符在内的所有字符。
什么是"回溯灾难"?如何避免?
回溯灾难(Catastrophic Backtracking)发生在正则引擎尝试大量可能的匹配路径时,导致性能急剧下降。常见于嵌套量词如 (a+)+。避免方法:1)使用非贪婪量词;2)避免嵌套量词;3)使用原子分组或占有量词(Python中有限支持);4)尽量使用更精确的模式。
\w 能匹配中文字符吗?
在Python 3中,默认使用Unicode模式,\w 可以匹配中文字符(以及所有Unicode字母)。如果需要只匹配ASCII字母、数字和下划线,可以使用 re.ASCII 标志,或者显式写成 [a-zA-Z0-9_]。
本文涉及AI创作
内容由AI创作,请仔细甄别