python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > python re模块和正则表达式

python中的re模块和正则表达式操作实例代码

作者:llllshuishuishui

Python是一种广泛使用的高级编程语言,它以其简洁、易读的语法而受到许多程序员的喜爱,这篇文章主要介绍了python中re模块和正则表达式操作的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在Python中,re模块是用于处理正则表达式的内置库。正则表达式是一种强大的字符串匹配和处理工具,它使用一种特定的模式来匹配、查找、替换或分割字符串。

一、正则表达式基础

1. 什么是正则表达式?

2. 核心元字符(基础语法)

元字符含义示例匹配结果
.匹配任意单个字符(除换行符 \na.bacba1ba@b(不匹配 ab
*前面的字符出现 0 次或多次ab*cac(b 0 次)、abbc(b 2 次)
+前面的字符出现 1 次或多次ab+cabc(b 1 次)、abbbc(b 3 次)
?前面的字符出现 0 次或 1 次(非贪婪)ab?cac(b 0 次)、abc(b 1 次)
{n}前面的字符出现 恰好 n 次a{2}baab(a 2 次)
{n,}前面的字符出现 至少 n 次a{2,}baabaaab(a≥2 次)
{n,m}前面的字符出现 n 到 m 次a{1,3}babaabaaab
[]字符集:匹配括号内任意一个字符[abc]abc
[^]否定字符集:匹配不在括号内的字符[^abc]d1@(不匹配 a/b/c)
|逻辑 “或”:匹配左边或右边的模式abc|defabc 或 def
()分组:将多个字符视为一个整体(ab)+ababab(ab 整体重复)
\转义字符:取消元字符的特殊含义a\.b(匹配 a.ba.b(不匹配 acb
^匹配字符串开头(单行模式)^abcabc123(开头是 abc)
$匹配字符串结尾(单行模式)abc$123abc(结尾是 abc)

3. 预定义字符集(简化写法)

预定义字符含义等价于
\d匹配任意数字(0-9)[0-9]
\D匹配任意非数字[^0-9]
\w匹配字母、数字、下划线(a-z, A-Z, 0-9, _)[a-zA-Z0-9_]
\W匹配非字母、数字、下划线[^a-zA-Z0-9_]
\s匹配空白字符(空格、Tab、换行等)[ \t\n\r\f\v]
\S匹配非空白字符[^ \t\n\r\f\v]
\b匹配单词边界(单词与非单词的分隔处)-
\B匹配非单词边界-

4. 贪婪匹配与非贪婪匹配

二、Python re 模块核心用法

re 模块是 Python 处理正则表达式的内置模块,提供了一系列操作函数。

使用前需导入:import re

1. 匹配函数(核心)

在 re 模块中,“匹配” 可以理解为:用正则表达式定义的 “模式” 去比对字符串,看字符串中是否存在符合该模式的部分,以及找到这些部分的过程

函数功能描述返回值
re.match(pattern, string)从字符串开头匹配模式,若不匹配则返回 None匹配对象(Match)或 None
re.search(pattern, string)在整个字符串中查找第一个匹配的子串,不要求从开头匹配。匹配对象(Match)或 None
re.findall(pattern, string)查找字符串中所有匹配的子串,返回列表。列表(元素为匹配的字符串)
re.finditer(pattern, string)查找所有匹配的子串,返回迭代器(每个元素是 Match 对象)。迭代器
re.sub(pattern, repl, string, count=0)将所有匹配的子串替换为 replcount 为替换次数(0 表示全部)。替换后的新字符串
re.subn(pattern, repl, string)与 sub 类似,但返回 (新字符串, 替换次数)元组 (new_str, count)
re.split(pattern, string, maxsplit=0)按匹配的子串分割字符串,maxsplit 为最大分割次数(0 表示全部分割)。分割后的列表

2. 匹配对象(Match)的常用方法

当 match 或 search 匹配成功时,返回 Match 对象,包含以下常用方法:

关键总结

  1. 用法re.match(正则模式, 目标字符串),必须从字符串开头匹配。
  2. 返回值
    • 匹配成功 → Match 对象(用 group() 提取内容,span() 看位置);
    • 匹配失败 → None(所以实际使用时通常会用 if result: 判断是否成功)。

匹配成功:

import re  # 必须先导入re模块

# 定义正则模式:匹配以 "he" 开头的字符串
pattern = r"he"
# 被匹配的字符串:开头是 "he"
string = "hello world"

# 用match进行匹配
result = re.match(pattern, string)

print(result)  
# 匹配成功 输出:<re.Match object; span=(0, 2), match='he'>

if result:  # 先判断是否匹配成功(避免None报错)
    print(result.group())  # 提取匹配到的字符串 → 输出 'he'
    print(result.span())   # 提取匹配的位置 → 输出 (0, 2)
    print(result.start())  # 匹配的起始索引 → 输出 0
    print(result.end())    # 匹配的结束索引 → 输出 2

匹配失败:

import re

pattern = r"he"
# 被匹配的字符串:开头是 's',不是 'he'
string = "she is a girl"

result = re.match(pattern, string)
print(result)  # 输出:None(表示匹配失败)

3. 带分组的模式

分组就是用 () 把正则模式中的某一部分 “包裹起来”,这部分会被视为一个 “独立单元”。

目的是帮我们从匹配到的内容中精准提取局部信息。

比如在模式 姓名:(\w+), 年龄:(\d+) 中:

方法作用
group(n)返回第 n 个分组的内容(n=0 或不填时,返回整个匹配结果)
groups()返回所有分组的内容,以元组形式呈现

示例

import re

# 模式:用 () 把手机号和验证码分别设为分组
pattern = r"手机号:(\d{11}), 验证码:(\d{4})"
string = "手机号:13812345678, 验证码:6666, 时间:10:00"

# 匹配成功,得到 Match 对象
result = re.match(pattern, string)
print(result)
# 输出:<re.Match object; span=(0, 25), match='手机号:13812345678, 验证码:6666'>

if result:
    # 1. group() 或 group(0):整个匹配的内容
    print(result.group())    # 输出:'手机号:13812345678, 验证码:6666'
    
    # 2. group(1):第一个分组(手机号)
    print(result.group(1))   # 输出:'13812345678'
    
    # 3. group(2):第二个分组(验证码)
    print(result.group(2))   # 输出:'6666'
    
    # 4. groups():所有分组,返回元组
    print(result.groups())   # 输出:('13812345678', '6666')

    print(result.span())  # 输出:(0, 25)

4. 编译正则表达式(re.compile())

当一个正则表达式需要多次使用时,建议先编译为 Pattern 对象,提升效率:

import re

# 编译模式(一次编译,多次使用)
pattern = re.compile(r"\d+")  # 匹配数字

# 使用编译后的模式调用方法
print(pattern.findall("a123b456"))  # 输出:['123', '456']
print(pattern.sub("num", "a123b456"))  # 输出:'anum bnum'

4. 常用标志位(修饰符)

通过标志位修改正则匹配的行为,可在函数中通过 flags 参数指定:

标志位

含义

re.I 或 re.IGNORECASE忽略大小写匹配(如 a 匹配 A
re.S 或 re.DOTALL让 . 匹配包括换行符 \n 在内的所有字符
re.M 或 re.MULTILINE让 ^ 匹配每行开头,$ 匹配每行结尾
re.X 或 re.VERBOSE忽略正则中的空格和注释(方便写复杂正则)

示例(忽略大小写):

print(re.findall(r"abc", "ABCabc", flags=re.I))  # 输出:['ABC', 'abc']

三、实战案例

1. 验证手机号(国内手机号规则:1 开头,共 11 位数字)
import re

def is_valid_phone(phone):
    pattern = r"^1\d{10}$"  # ^开头,1 followed by 10 digits,$结尾
    return bool(re.match(pattern, phone))

print(is_valid_phone("13812345678"))  # 输出:True
print(is_valid_phone("1234567890"))   # 输出:False(长度不够)

2. 提取文本中的所有邮箱

import re

text = "联系邮箱:a@qq.com,备用邮箱:b.c@163.com"
pattern = r"\w+(\.\w+)*@\w+(\.\w+)+"  # 简单邮箱规则
emails = re.findall(pattern, text)
# 注意:findall 会优先返回分组内容,若需整体匹配,可给模式加括号
pattern = r"(\w+(\.\w+)*@\w+(\.\w+)+)"
emails = re.findall(pattern, text)
print([email[0] for email in emails])  # 输出:['a@qq.com', 'b.c@163.com']

3. 替换日期格式(2023-10-01→10/01/2023)

import re

text = "今天是2023-10-01,明天是2023-10-02"
pattern = r"(\d{4})-(\d{2})-(\d{2})"  # 分组:年-月-日
new_text = re.sub(pattern, r"\2/\3/\1", text)  # 用分组2/3/1替换
print(new_text)  # 输出:'今天是10/01/2023,明天是10/02/2023'

四、注意事项

  1. 转义字符:在 Python 字符串中,\ 是转义符,因此正则中的 \d 需写成 r"\d"(raw 字符串),避免被 Python 解释器转义。例:r"\d+" 正确,"\d+" 错误(Python 会将 \d 视为转义符,导致正则失效)。

  2. 贪婪与非贪婪:处理长文本时,注意 *+ 的贪婪特性可能导致匹配结果不符合预期,必要时加 ? 切换为非贪婪模式。

  3. 性能问题:复杂正则(如嵌套分组、大量 *)可能导致匹配效率低,建议:

    • 用 re.compile() 编译常用正则;
    • 避免过度复杂的模式(必要时拆分处理)。
  4. 调试工具:推荐使用在线正则工具辅助调试,如 RegExr(可视化匹配过程)。

一个疑惑:为什么pattern前面要加r?

r 是 Python 中 “原始字符串(raw string)” 的标记,作用是让字符串中的反斜杠 \ 不被转义

问题的根源:\(反斜杠)在Python 字符串和正则表达式中都有特殊含义,这就容易冲突。

不加r会出什么问题?

假设我们想用正则匹配一个 \ 字符(比如文件路径中的 C:\test),看看不加 r 会怎样:

import re

# 目标:匹配字符串中的 \ 字符
string = "C:\\test"  # Python 中,用 \\ 表示一个真正的 \(因为单个 \ 是转义符)

# 不加 r 的正则模式:想写 \ 匹配真正的 \,但 Python 会先解析
pattern = "\\\\"  # 解释:
# 1. Python 先处理这个字符串:\\ 会被解析成一个 \
# 2. 所以传给正则的实际是 \\(正则中,\\ 才表示匹配一个 \)

result = re.findall(pattern, string)
print(result)  # 输出:['\\'](终于匹配到了 \)

看到了吗?为了匹配一个 \,不加 r 时,正则模式需要写 \\\\(4 个反斜杠),这太麻烦了!

加了r之后:清爽多了!

r 告诉 Python:“这个字符串是原始字符串,里面的 \ 不要当转义符,原样传给正则就好!”

还是上面的例子,加 r 后:

import re

string = "C:\\test"  # 依然用 \\ 表示一个 \(因为这是 Python 字符串的规则)

# 加 r 的正则模式:直接写 \\ 即可
pattern = r"\\"  # 解释:
# 1. 因为加了 r,Python 不处理 \,直接把 \\ 传给正则
# 2. 正则中,\\ 表示匹配一个 \,正好符合需求

result = re.findall(pattern, string)
print(result)  # 输出:['\\'](同样匹配成功,但模式简单多了!)

再举一个常用例子:正则中的\d

\d 在正则中表示 “匹配数字”。如果不加 r

import re

pattern = "\d"  # 不加 r,Python 会检查 \d 是否是它的转义符(但 Python 中没有 \d 这个转义符,所以会“侥幸”原样传给正则)
result = re.findall(pattern, "123")
print(result)  # 输出:['1', '2', '3'](这次碰巧没出错)

但如果正则中用到 \n(正则中表示 “匹配换行符”),不加 r 就会出问题:

import re

# 目标:匹配字符串中的换行符 \n
string = "第一行\n第二行"  # 包含一个换行符

# 不加 r 的模式:\n 会被 Python 解析成真正的换行符,而不是正则需要的 \n
pattern = "\n"  # Python 把它变成了一个换行符,传给正则的是“换行符”
result = re.findall(pattern, string)
print(result)  # 输出:['\n'](看似成功,但逻辑错了!正则实际收到的是换行符,而不是 \n 规则)

# 加 r 的模式:\n 原样传给正则,正则知道这是“匹配换行符”的规则
pattern = r"\n"  # 正则收到的是 \n,正确执行匹配
result = re.findall(pattern, string)
print(result)  # 输出:['\n'](同样成功,但逻辑正确)

总结:为什么要加r?

到此这篇关于python中的re模块和正则表达式的文章就介绍到这了,更多相关python re模块和正则表达式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文