pin_drop当前位置:知识文库 ❯ 图文

正则表达式基础语法详解 - Python re模块入门指南

一、什么是正则表达式

正则表达式(Regular Expression,简称 Regex 或 RE)是一种用于匹配、查找、替换文本中特定模式的强大工具。它使用一组特殊字符来定义搜索模式,能够高效地完成字符串验证、数据提取、文本替换等任务。

在Python中,我们可以使用内置的 re 模块来处理正则表达式。正则表达式广泛应用于表单验证、日志分析、数据清洗、爬虫提取等场景。


二、元字符详解

元字符是正则表达式中具有特殊含义的字符,它们是构建正则表达式的核心元素。以下是常用的元字符及其含义:

元字符 含义 示例 匹配内容
. 匹配任意字符(除换行符) a.c abc、a1c、a c
^ 匹配字符串开头 ^Hello Hello world(开头)
$ 匹配字符串结尾 world$ Hello world(结尾)
* 匹配前一个字符0次或多次 ab* a、ab、abb
+ 匹配前一个字符1次或多次 ab+ ab、abb(不匹配a)
? 匹配前一个字符0次或1次 ab? a、ab
| 逻辑或 cat|dog cat、dog

代码示例


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)用于匹配一组字符中的任意一个。使用方括号 [] 来定义字符类。

常用字符类

字符类 含义 等价写法
\d 匹配任意数字 [0-9]
\w 匹配单词字符(字母、数字、下划线) [a-zA-Z0-9_]
\s 匹配空白字符(空格、制表符、换行) [ \t\n\r\f\v]
\D 匹配非数字 [^0-9]
\W 匹配非单词字符 [^a-zA-Z0-9_]
\S 匹配非空白字符 [^ \t\n\r\f\v]

代码示例


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']

四、量词

量词用于指定前面的字符或字符类需要匹配的次数。量词默认是"贪婪"的,会尽可能多地匹配字符。

量词 含义 示例
{n} 恰好匹配n次 \d{4} 匹配4位数字
{n,} 匹配至少n次 \d{2,} 匹配至少2位数字
{n,m} 匹配n到m次 \d{2,4} 匹配2到4位数字

代码示例


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解析等场景中非常有用。


五、锚点

锚点用于匹配位置而不是字符,它们不消耗任何字符,只是断言某个位置的存在。

锚点 含义 说明
^ 字符串开头 多行模式下匹配行首
$ 字符串结尾 多行模式下匹配行尾
\b 单词边界 匹配单词的开头或结尾位置
\B 非单词边界 匹配不在单词边界的位置

代码示例


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位、包含大写字母、小写字母、数字和特殊字符(!@#$%^&*)。

练习2

编写一个函数 extract_links(html),从HTML文本中提取所有的 链接地址和链接文本。

常见问题

为什么在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_]。

标签: 正则表达式 元字符 量词 字符类 Python入门 字符串处理 分组捕获

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python生成器高级应用 - 管道模式、协程与send方法详解 下一篇: re.match详解 - Python正则表达式从头匹配方法使用指南

poll相关推荐