第一部分:Python 文件IO基础
1 文件 IO 概念
1.1 基本定义
-
文件 IO:即文件 Input/Output,实现程序与操作系统之间的文件数据交互。
-
输入流(Input):将文件中的数据读取到内存(程序)中。
-
输出流(Output):将内存(程序)中的数据写入到文件中。
-
核心函数 :Python 内置
open()函数,用于打开文件并返回数据流(输入流 / 输出流),操作失败会抛出OSError异常。
1.2 文件分类(操作系统层面)
| 文件类型 | 底层组成 | 校验方式 | 典型示例 |
|---|---|---|---|
| 字符文件 | 字符(文本) | 记事本打开无乱码 | 源代码文件(.py)、配置文件(.txt)、文档(.md) |
| 字节文件 | 字节 / 二进制 | 记事本打开会乱码 | 图片(.jpg/.png)、音频(.mp3)、视频(.mp4)、压缩包(.zip) |
1.3 open () 函数
1.3.1 语法
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
1.3.2 参数说明
| 参数 | 功能 | 关键注意事项 |
|---|---|---|
| file | 文件路径(相对路径 / 绝对路径) | Windows 路径分隔符可用 \\ 或 /(避免转义问题) |
| mode | 操作模式(控制读写方式、文件类型) | 由 "操作符 + 类型符" 组合,默认 r(文本读) |
| encoding | 字符编码(仅字符文件需要) | 常用 UTF-8,字节文件无需指定 |
| buffering | 缓冲策略 | 默认 -1(系统自动缓冲),无需手动设置 |
1.3.3 mode 参数组合
| 模式组合 | 功能描述 | 适用文件类型 |
|---|---|---|
| r / rt | 文本读(默认) | 字符文件 |
| w / wt | 文本写(覆盖原有内容,文件不存在则创建) | 字符文件 |
| a / at | 文本追加(在文件尾部写入,文件不存在则创建) | 字符文件 |
| rb | 字节读 | 字节文件 |
| wb | 字节写(覆盖原有内容) | 字节文件 |
| ab | 字节追加 | 字节文件 |
| r+ / rt+ | 文本读写(可同时读和写) | 字符文件 |
| rb+ | 字节读写 | 字节文件 |
| x / xt / xb | 新建文件并写(文件已存在则报错) | 字符 / 字节文件 |
2 字符文件操作(文本文件)
字符文件操作需指定 encoding(如 UTF-8),避免中文乱码。核心流程:打开文件 → 读写数据 → 关闭文件。
2.1 写入字符文件
2.1.1 基础语法(open () + close ())
# 1. 准备文本数据
content = "hello python!\n你好,Python 文件IO!"
# 2. 打开文件:mode="wt"(文本写),指定编码 UTF-8
file = open("test01.txt", mode="wt", encoding="UTF-8")
# 3. 写入数据:write() 接收字符串
file.write(content)
# 4. 关闭文件(必须执行,释放资源)
file.close()
2.1.2 高级语法(with 语句,自动关闭)
with 语句会自动封装 "打开→操作→关闭" 流程,避免遗漏 close(),优先使用:
# 自动打开文件,执行完代码块后自动关闭
with open("test02.txt", mode="wt", encoding="UTF-8") as file:
file.write("使用 with 语句写入文本数据")
2.2 读取字符文件
2.2.1 基础读取(读取全部内容)
# mode="rt" 可省略(默认 r),必须指定 encoding
with open("test01.txt", encoding="UTF-8") as file:
# read():读取文件全部内容,返回字符串
data = file.read()
print("读取结果:")
print(data)
2.2.2 进阶读取方法
| 方法 | 功能 | 示例 |
|---|---|---|
| read(size) | 读取指定长度的字符(size 为字符数) | file.read (10) → 读取前 10 个字符 |
| readline() | 读取一行数据(以 \n 为分隔) | 逐行读取大文件时避免内存溢出 |
| readlines() | 读取所有行,返回列表(每行作为一个元素) | lines = file.readlines () → 列表推导式处理每行 |
2.2.3 逐行读取大文件(推荐)
# 逐行读取,适合超大文本文件(避免一次性加载全部内容)
with open("large_file.txt", encoding="UTF-8") as file:
for line in file: # 直接迭代文件对象,逐行读取
print(line.strip()) # strip() 去除换行符和空格
2.3 字符文件操作注意事项
-
写入后必须关闭文件(或用
with语句),否则数据可能未真正写入(缓冲区未刷新)。 -
读取时必须指定正确的
encoding,否则中文会乱码。 -
若文件不存在,
r模式会报错,w/a/x模式会自动创建文件。
3 字节文件操作(二进制文件)
字节文件操作模式需带 b(如 rb/wb),无需指定 encoding ,数据以字节串(bytes 类型,前缀 b)处理。
3.1 读取字节文件(如图片、视频)
# 读取图片文件(字节文件)
with open("test.jpg", mode="rb") as file:
# read() 返回字节串(bytes 类型)
byte_data = file.read()
print("字节数据长度:", len(byte_data))
print("前10个字节:", byte_data[:10]) # 切片查看部分字节
3.2 写入字节文件
字节数据需用 b'' 表示(或通过 str.encode(encoding) 转换):
# 方式1:直接定义字节串
byte_content1 = b"hello world!" # 英文直接加 b 前缀
# 方式2:字符串编码为字节串(中文需指定编码)
byte_content2 = "你好,字节文件!".encode("UTF-8")
# 写入字节文件(mode="wb")
with open("test03.bin", mode="wb") as file:
file.write(byte_content1)
file.write(b"\n") # 字节换行符
file.write(byte_content2)
3.3 字节与字符串转换
| 转换方向 | 方法 | 示例 |
|---|---|---|
| 字符串 → 字节 | str.encode(encoding) | "中文".encode ("UTF-8") → 字节串 |
| 字节 → 字符串 | bytes.decode(encoding) | b'\xe4\xb8\xad\xe6\x96\x87'.decode ("UTF-8") → 字符串 |
4 综合实战:文件复制(支持所有文件类型)
文件复制的核心是字节流操作(适配字符文件和字节文件),大文件需分块读取,避免内存溢出。
4.1 基础版:大文件分块复制
import os
def copy_file(source_path, target_path, chunk_size=1024*1024):
"""
大文件分块复制(支持所有文件类型)
:param source_path: 源文件路径(必须是完整文件路径)
:param target_path: 目标文件路径(必须包含文件名)
:param chunk_size: 分块大小(默认 1MB,可调整)
"""
# 检查源文件是否存在
if not os.path.exists(source_path):
raise FileNotFoundError(f"源文件不存在:{source_path}")
# 确保目标文件所在目录存在
target_dir = os.path.dirname(target_path)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
# 分块读写(字节模式)
with open(source_path, "rb") as src_file, open(target_path, "wb") as tgt_file:
while True:
# 每次读取 chunk_size 字节
chunk = src_file.read(chunk_size)
if not chunk: # 读取到空字节串,说明文件结束
break
tgt_file.write(chunk) # 分块写入
print(f"文件复制完成:{source_path} → {target_path}")
# 调用函数:复制 ISO 镜像文件
if __name__ == "__main__":
source = "F:/BaiduNetdiskDownload/ubuntu-24.10-desktop-amd64.iso"
target = "D:/bat/ubuntu_copy.iso"
copy_file(source, target)
4.2 进阶版:带进度提示的复制(tqdm 进度条)
需先安装:pip install tqdm
from tqdm import tqdm
import os
def copy_file_with_progress(source_path, target_path, chunk_size=1024*1024):
"""带进度条的文件复制"""
if not os.path.exists(source_path):
raise FileNotFoundError(f"源文件不存在:{source_path}")
# 获取源文件总大小(用于进度计算)
total_size = os.path.getsize(source_path)
target_dir = os.path.dirname(target_path)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
# 打开文件并创建进度条
with open(source_path, "rb") as src_file, open(target_path, "wb") as tgt_file:
# tqdm 进度条配置:总大小、单位、自动缩放
with tqdm(
total=total_size,
unit="B",
unit_scale=True,
desc=f"复制 {os.path.basename(source_path)}"
) as progress_bar:
while True:
chunk = src_file.read(chunk_size)
if not chunk:
break
tgt_file.write(chunk)
# 更新进度条(每次更新读取的字节数)
progress_bar.update(len(chunk))
print(f"\n✅ 文件复制完成:{target_path}")
# 测试
if __name__ == "__main__":
source = "F:/BaiduNetdiskDownload/ubuntu-24.10-desktop-amd64.iso"
target = "D:/bat/ubuntu_with_progress.iso"
copy_file_with_progress(source, target)
4.3 文件复制常见问题
-
路径问题 :Windows 路径分隔符可用
\\(转义)或/(推荐);目标路径必须包含文件名。 -
权限问题:操作系统目录需管理员权限。
-
大文件处理:分块大小推荐 1MB~8MB,平衡速度与内存。
5 抽象数据的 IO 操作(序列化与反序列化)
抽象数据:列表、字典、元组等非字符 / 字节类型。
-
序列化:对象 → 文件可存储格式
-
反序列化:文件 → 原始对象
5.1 pickle 模块(字节序列化,支持所有 Python 对象)
5.1.1 序列化(对象 → 字节文件)
import pickle
# 准备抽象数据(字典、列表混合)
user_data = {
"admin": {"username": "admin", "password": "123456", "age": 25},
"manager": {"username": "manager", "password": "654321", "roles": ["user", "admin"]}
}
# 序列化到字节文件(mode="wb")
with open("user_data.dat", mode="wb") as file:
pickle.dump(user_data, file) # dump():将对象写入文件
print("✅ 数据序列化完成")
5.1.2 反序列化(字节文件 → 对象)
import pickle
# 从字节文件反序列化
with open("user_data.dat", mode="rb") as file:
loaded_data = pickle.load(file) # load():读取文件并恢复对象
# 验证数据(类型和内容不变)
print("反序列化后数据类型:", type(loaded_data))
print("反序列化后数据:", loaded_data)
print("admin 密码:", loaded_data["admin"]["password"])
5.2 json 模块(字符序列化,跨语言兼容)
5.2.1 序列化(对象 → JSON 文件)
import json
# 准备基础抽象数据(仅支持 JSON 兼容类型)
product_data = {
"id": 1001,
"name": "Python 编程从入门到实践",
"price": 89.0,
"tags": ["编程", "Python", "入门"],
"is_stock": True
}
# 序列化到 JSON 文件(mode="wt",指定 encoding)
with open("product.json", mode="wt", encoding="UTF-8") as file:
# indent:格式化输出;ensure_ascii=False:支持中文
json.dump(product_data, file, indent=4, ensure_ascii=False)
print("✅ JSON 序列化完成")
5.2.2 反序列化(JSON 文件 → 对象)
import json
# 从 JSON 文件反序列化
with open("product.json", encoding="UTF-8") as file:
loaded_product = json.load(file)
print("反序列化后数据类型:", type(loaded_product))
print("产品名称:", loaded_product["name"])
print("产品标签:", loaded_product["tags"])
5.3 序列化模块对比
| 模块 | 序列化格式 | 支持数据类型 | 跨语言兼容 | 适用场景 |
|---|---|---|---|---|
| pickle | 字节流 | 所有 Python 对象 | 否(仅 Python) | 内部数据持久化 |
| json | JSON 字符串 | 基础类型 | 是 | 跨语言交互 |
| marshal | 字节流 | 基础类型 | 否 | 了解即可 |
| shelve | 数据库文件 | 字典格式 | 否 | 简单键值存储 |
6 综合任务代码
6.1 任务 1:文本文件合并工具(按文件名顺序)
import os
def merge_txt_files(output_filename="合并结果.txt"):
all_files = os.listdir(".")
txt_files = [f for f in all_files if f.endswith(".txt")]
if not txt_files:
print("当前目录没有找到任何 .txt 文件!")
return
txt_files.sort()
print(f"即将合并以下文件:{txt_files}")
with open(output_filename, "w", encoding="utf-8") as out_file:
for file in txt_files:
try:
with open(file, "r", encoding="utf-8") as in_file:
out_file.write(f"===== 来自文件:{file} =====\n")
out_file.write(in_file.read())
out_file.write("\n\n")
print(f"已合并:{file}")
except Exception as e:
print(f"读取文件 {file} 失败:{e}")
print(f"\n✅ 合并完成!结果保存在:{output_filename}")
if __name__ == "__main__":
merge_txt_files()
6.2 任务 2:JSON 用户信息管理系统
import json
import os
USER_FILE = "users.json"
class UserManager:
def __init__(self):
self.users = []
self.load_from_file()
def add_user(self, user_id, name, age, phone):
for user in self.users:
if user["id"] == user_id:
print("❌ 用户ID已存在!")
return False
new_user = {"id": user_id, "name": name, "age": age, "phone": phone}
self.users.append(new_user)
print("✅ 用户添加成功")
return True
def find_user(self, user_id):
for user in self.users:
if user["id"] == user_id:
return user
return None
def save_to_file(self):
with open(USER_FILE, "w", encoding="utf-8") as f:
json.dump(self.users, f, ensure_ascii=False, indent=4)
print("✅ 数据已保存到 users.json")
def load_from_file(self):
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
self.users = json.load(f)
print(f"✅ 从文件加载了 {len(self.users)} 条用户数据")
else:
self.users = []
if __name__ == "__main__":
um = UserManager()
um.add_user(101, "张三", 20, "13800138000")
um.add_user(102, "李四", 22, "13900139000")
user = um.find_user(101)
print("\n查询结果:", user)
um.save_to_file()
6.3 任务 3:支持断点续传的文件复制
import os
import json
RECORD_FILE = "copy_record.json"
class ResumableCopy:
def __init__(self, source, target, chunk_size=1024*1024):
self.source = source
self.target = target
self.chunk_size = chunk_size
self.record = self.load_record()
def load_record(self):
if os.path.exists(RECORD_FILE):
with open(RECORD_FILE) as f:
return json.load(f)
return {}
def save_record(self, position):
self.record[self.source] = position
with open(RECORD_FILE, "w") as f:
json.dump(self.record, f, indent=2)
def start_copy(self):
if not os.path.exists(self.source):
print("❌ 源文件不存在")
return
total_size = os.path.getsize(self.source)
start_pos = self.record.get(self.source, 0)
if start_pos >= total_size:
print("✅ 文件已完整复制,无需继续")
if self.source in self.record:
del self.record[self.source]
self.save_record(0)
return
print(f"⏯️ 从断点 {start_pos} 字节处继续复制")
with open(self.source, "rb") as src, open(self.target, "ab") as tgt:
src.seek(start_pos)
while True:
chunk = src.read(self.chunk_size)
if not chunk:
break
tgt.write(chunk)
start_pos += len(chunk)
self.save_record(start_pos)
print(f"\r复制进度:{start_pos / total_size * 100:.1f}%", end="")
print("\n✅ 复制完成!")
if self.source in self.record:
del self.record[self.source]
self.save_record(0)
if __name__ == "__main__":
copy = ResumableCopy(source="大文件.zip", target="大文件_副本.zip")
copy.start_copy()
推荐在线正则测试工具:
1.Regex101.htm(支持语法提示、匹配结果实时预览)
第二部分 正则表达式
1 认识正则表达式
1.1 核心概念
-
正则表达式(Regular Expression):简称 regex/regexp,是一种用于匹配、查找、替换文本的模式语言。
-
核心价值:用简洁的表达式描述复杂的文本规则,高效处理字符串(匹配验证、提取筛选、替换清洗)。
-
适用场景:表单验证(手机号、邮箱)、日志分析、数据爬虫、文本编辑器查找替换等。
1.2 正则表达式的优势
| 处理方式 | 实现逻辑 | 代码复杂度 | 灵活性 | 效率 |
|---|---|---|---|---|
| 普通字符串方法(in/split ()/replace ()) | 逐字符比对,需手动组合逻辑 | 高(复杂规则需多步判断) | 低(仅支持固定匹配) | 简单场景高效 |
| 正则表达式 | 编译模式后批量匹配,支持复杂规则 | 低(一行表达式搞定复杂规则) | 高(支持模糊匹配、动态规则) | 复杂场景高效 |
1.3 简单示例
| 需求 | 正则表达式 | 匹配结果 |
|---|---|---|
| 匹配手机号(11 位数字,以 13/14/15/17/18/19 开头) | ^1[3-9]\d{9}$ |
匹配:13800138000,不匹配:12345678901/1380013800 |
| 匹配邮箱(以字母 / 数字开头,支持 @xx.com/xx.cn 等) | ^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z]{2,6}$ |
匹配:test123@qq.com,不匹配:test@.com/test#163.cn |
| 提取文本中的所有数字 | \d+ |
文本:abc123def45.67 → 提取:123/45/67 |
2 正则表达式基础语法
正则表达式的核心是元字符(具有特殊含义的字符),通过元字符组合成匹配规则。
2.1 元字符分类与说明
2.1.1 匹配单个字符的元字符
| 元字符 | 功能 | 示例 | 匹配结果 |
|---|---|---|---|
. |
匹配任意单个字符(除换行符 \n) | a.b |
匹配:acb/a+b/a b,不匹配:abb/a\nb |
[] |
匹配括号内的任意一个字符 | [abc] |
匹配:a/b/c |
[^] |
匹配括号外的任意一个字符(取反) | [^abc] |
匹配:d/1/!,不匹配:a/b/c |
\d |
匹配任意数字(等价于 [0-9]) | \d |
匹配:0/5/9 |
\D |
匹配非数字(等价于 [^0-9]) | \D |
匹配:a/!/,不匹配:0-9 |
\w |
匹配字母、数字、下划线(等价于 [a-zA-Z0-9_]) | \w |
匹配:A/3/_,不匹配:!/@/ |
\W |
匹配非字母、数字、下划线 | \W |
匹配:!/@/,不匹配:a-zA-Z0-9_ |
\s |
匹配空白字符(空格、制表符 \t、换行符 \n 等) | a\sb |
匹配:a b/a\tb,不匹配:ab/a_b |
\S |
匹配非空白字符 | a\Sb |
匹配:acb/a_b,不匹配:a b/a\tb |
\b |
单词边界(匹配单词开头 / 结尾,无实际字符) | \bhello\b |
匹配:hello world/say hello,不匹配:helloworld/hello_123 |
\B |
非单词边界 | \Bhello\B |
匹配:helloworld/hello_123,不匹配:hello world |
2.1.2 匹配数量的量词(限定前面元字符的出现次数)
| 量词 | 功能 | 示例 | 匹配结果 |
|---|---|---|---|
* |
匹配前面的元字符 0 次或多次(贪婪) | ab* |
匹配:a/ab/abb/abbb |
+ |
匹配前面的元字符 1 次或多次(贪婪) | ab+ |
匹配:ab/abb/abbb,不匹配:a |
? |
匹配前面的元字符 0 次或 1 次(贪婪) | ab? |
匹配:a/ab,不匹配:abb |
{n} |
匹配前面的元字符恰好 n 次 | a{3} |
匹配:aaa,不匹配:aa/aaaa |
{n,} |
匹配前面的元字符至少 n 次(贪婪) | a{2,} |
匹配:aa/aaa/aaaa,不匹配:a |
{n,m} |
匹配前面的元字符 n~m 次(贪婪) | a{2,4} |
匹配:aa/aaa/aaaa,不匹配:a/aaaaa |
2.1.3 匹配位置的锚定符(不匹配字符,仅匹配位置)
| 锚定符 | 功能 | 示例 | 匹配结果 |
|---|---|---|---|
^ |
匹配字符串开头(多行模式下匹配每行开头) | ^hello |
匹配:hello world,不匹配:world hello |
$ |
匹配字符串结尾(多行模式下匹配每行结尾) | world$ |
匹配:hello world,不匹配:world hello |
\A |
匹配字符串绝对开头(不受多行模式影响) | \Ahello |
仅匹配:hello world(字符串首字符为 h) |
\Z |
匹配字符串绝对结尾(不受多行模式影响) | world\Z |
仅匹配:hello world(字符串尾字符为 d) |
2.1.4 分组与逻辑运算符
| 符号 | 功能 | 示例 | 匹配结果 | ||
|---|---|---|---|---|---|
() |
分组(将多个元字符视为一个整体,支持捕获) | (ab)+ |
匹配:ab/abab/ababab | ||
| ` | ` | 逻辑或(匹配任意一个分组) | `abc | def` | 匹配:abc/def,不匹配:abd/cde |
\num |
反向引用(引用第 num 个分组的匹配结果) | (\w+)\s+\1 |
匹配:hello hello/123 123,不匹配:hello world | ||
(?:) |
非捕获分组(仅分组,不捕获结果,节省资源) | (?:ab)+ |
匹配:ab/abab,但无法通过 \1 引用 |
2.2 转义字符(\)
当需要匹配元字符本身(如 ./*/()时,需用 \ 转义:
-
匹配
.:\.(如192\.168\.1\.1匹配 IP 地址中的点) -
匹配
*:\*(如a\*b匹配a*b) -
匹配
(:\(`(如 `\(123\)匹配(123))
2.3 常用正则表达式
| 需求 | 正则表达式 | 说明 | ||||
|---|---|---|---|---|---|---|
| 手机号 | ^1[3-9]\d{9}$ |
11 位数字,以 13/14/15/17/18/19 开头 | ||||
| 固定电话 | ^0\d{2,3}-\d{7,8}$ |
格式:010-12345678 / 0571-87654321 | ||||
| 邮箱 | ^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z]{2,6}$ |
支持常见域名(.com/.cn/.org 等) | ||||
| 身份证号(18 位) | ^[1-9]\d{16}[\dXx]$ |
最后一位可为数字或 X/x | ||||
| IP 地址(IPv4) | `^((25[0-5] | 2[0-4]\d | [01]?\d\d?).){3}(25[0-5] | 2[0-4]\d | [01]?\d\d?)$` | 匹配合法 IPv4 地址 |
| 中文汉字 | ^[\u4e00-\u9fa5]+$ |
仅匹配纯中文(不含字母、数字、符号) | ||||
| 强密码 | ^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,16}$ |
含大小写、数字、符号,8-16 位 | ||||
| URL | `^(https? | ftp)://([\w-]+.)+[\w-]+(/[\w-./?%&=]*)?$` | 匹配 http/https/ftp 网址 |
3 Python re 模块
Python 内置 re 模块提供正则表达式的核心操作。
3.1 re 模块函数
| 函数 | 功能 | 返回值 | 关键说明 |
|---|---|---|---|
re.match() |
从字符串开头匹配 | 匹配对象 / None | 仅匹配开头 |
re.search() |
任意位置首次匹配 | 匹配对象 / None | 扫描整个字符串 |
re.findall() |
提取所有匹配结果 | 列表 | 无匹配返回空列表 |
re.finditer() |
提取所有匹配(迭代器) | 迭代器 | 大量结果节省内存 |
re.sub() |
替换匹配内容 | 新字符串 | 支持字符串 / 函数替换 |
re.split() |
按模式分割字符串 | 列表 | 支持自定义分割符 |
re.compile() |
编译正则模式 | 模式对象 | 多次复用提高效率 |
3.2 匹配对象常用方法
| 方法 | 功能 | 示例 |
|---|---|---|
group() |
返回整个匹配字符串 | match.group() → 123 |
group(n) |
返回第 n 个分组 | match.group(1) → 2024 |
groups() |
返回所有分组(元组) | ('2024','10','01') |
start() |
匹配起始索引 | 0 |
end() |
匹配结束索引 | 7 |
span() |
匹配索引范围 | (0,7) |
3.3 实战示例
3.3.1 re.match () 从开头匹配
import re
pattern = r'^1[3-9]\d{9}$'
string1 = '13800138000'
string2 = 'tel:13800138000'
print(re.match(pattern, string1).group())
print(re.match(pattern, string2))
3.3.2 re.search () 首次匹配
pattern = r'\d+'
string = 'abc123def456'
print(re.search(pattern, string).group())
3.3.3 re.findall () 提取所有
pattern = r'\d+\.?\d*'
string = '价格:99元,折扣:0.85,最终价格:84.15元'
print(re.findall(pattern, string))
3.3.4 re.sub () 替换内容
# 敏感词替换
pattern = r'垃圾'
string = '这个产品真垃圾!垃圾商家!'
print(re.sub(pattern, '***', string))
# 函数替换
def add_brackets(match):
return f'({match.group()})'
pattern = r'\d+'
string = 'a123b456c'
print(re.sub(pattern, add_brackets, string))
3.3.5 re.split () 分割字符串
pattern = r'\s+'
string = 'hello world\tpython\nregex'
print(re.split(pattern, string))
3.3.6 re.compile () 编译复用
pattern = re.compile(r'[a-zA-Z]+')
string1 = '123abc456def'
string2 = '789ghi012jkl'
print(pattern.findall(string1))
print(pattern.findall(string2))
3.4 flags 模式修饰符
| flags 值 | 功能 | 示例 |
|---|---|---|
re.I |
忽略大小写 | re.search(r'abc','ABC',re.I) |
re.M |
多行模式 | ^/$ 匹配每行开头结尾 |
re.S |
单行模式 | . 匹配换行 |
re.X |
允许注释空格 | 复杂正则可读性更高 |
示例:
pattern = r'''
^1[3-9]
\d{9}$
'''
string = '13800138000'
print(re.match(pattern, string, re.X).group())
4 正则表达式高级用法
4.1 贪婪匹配与非贪婪匹配
-
贪婪:尽可能多匹配(默认)
-
非贪婪 :量词后加
?,尽可能少匹配
| 表达式 | 文本 | 贪婪结果 | 非贪婪结果 |
|---|---|---|---|
a.*b |
aabcbcd | aabcb | - |
a.*?b |
aabcbcd | - | aab |
示例:
html = '<div>正则</div><div>实战</div>'
greedy = r'<div.*</div>'
non_greedy = r'<div.*?</div>'
print(re.findall(greedy, html))
print(re.findall(non_greedy, html))
4.2 分组与反向引用
4.2.1 捕获分组
pattern = r'(\d{4})-(\d{2})-(\d{2})'
string = '2024-10-01'
res = re.search(pattern, string)
print(res.group(1), res.group(2), res.group(3))
4.2.2 反向引用
pattern = r'(\w+)\s+\1'
print(re.search(pattern, 'hello hello').group())
4.2.3 非捕获分组
pattern = r'(?:ab)+'
print(re.search(pattern, 'abab').group())
4.3 零宽断言(位置匹配)
| 断言 | 功能 | 示例 |
|---|---|---|
(?=pattern) |
后面满足 | a(?=b) |
(?!pattern) |
后面不满足 | a(?!b) |
(?<=pattern) |
前面满足 | (?<=a)b |
(?<!pattern) |
前面不满足 | (?<!a)b |
示例:
pattern = r'\d+\.?\d*(?=元)'
string = '价格:99元,折扣价:84.5元'
print(re.findall(pattern, string))
5 常见场景案例
5.1 表单验证(手机号 + 邮箱)
import re
def validate_phone(phone):
return bool(re.match(r'^1[3-9]\d{9}$', phone))
def validate_email(email):
return bool(re.match(r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z]{2,6}$', email))
5.2 日志分析(提取 IP、时间)
log = '''192.168.1.1 - - [01/Oct/2024:12:00:00 +0800] "GET /index.html" 200'''
pattern = r'(\d+\.\d+\.\d+\.\d+).*?\[([^\]]+)\] "(\w+) ([^"]+)" (\d+)'
for res in re.findall(pattern, log):
print(f"IP:{res[0]}, 时间:{res[1]}")
5.3 文本清洗(去 HTML、多余空格)
def clean_text(text):
text = re.sub(r'<[^>]+>', '', text)
text = re.sub(r'\s+', ' ', text).strip()
return text
5.4 提取图片 URL
html = '<img src="https://example.com/img1.jpg">'
pattern = r'<img.*?src="([^"]+)"'
print(re.findall(pattern, html))
6 正则表达式常见误区
6.1 误区 1:忘记转义元字符
-
错误:
192.168.1.1 -
正确:
192\.168\.1\.1
6.2 误区 2:混淆 ^/$ 与 \A/\Z
-
^/$受多行模式影响 -
\A/\Z始终匹配整个字符串
6.3 误区 3:过度使用贪婪匹配
- 提取标签优先使用
.*?
6.4 误区 4:忽略大小写与换行
-
忽略大小写:
re.I -
匹配换行:
re.S
6.5 性能优化
-
多次使用:
re.compile() -
复杂分组:非捕获分组
(?:) -
大数据:
re.finditer()
7 任务代码
7.1 任务 1:身份证验证 + 提取生日
import re
def check_id_card(id_str):
pattern = r'^[1-9]\d{16}[\dXx]$'
if not re.fullmatch(pattern, id_str):
return False, "格式错误"
birth = id_str[6:14]
return True, f"{birth[:4]}-{birth[4:6]}-{birth[6:8]}"
7.2 任务 2:提取 URL 域名
import re
def get_domain_from_url(url):
res = re.search(r'https?://([^/]+)', url)
return res.group(1) if res else None
7.3 任务 3:清洗纯中文文本
import re
def clean_chinese_text(text):
text = re.sub(r'<[^>]+>', '', text)
return re.sub(r'[^\u4e00-\u9fa5,。!?;:""''()【】]', '', text).strip()
7.4 任务 4:手机号脱敏
import re
def mask_phone(phone):
return re.sub(r'(\d{3})\d{4}(\d{4})', r'\1****\2', phone)
第三部分 Python进阶
1 文件操作进阶
文件操作是 Python 数据处理与项目开发的基础,进阶内容聚焦 非文本文件处理、大文件高效处理、路径灵活适配 三大核心场景,解决基础文件操作的局限性。
1.1 二进制文件读写
适用于图片、视频、音频、压缩包等非文本文件,核心是通过二进制模式(rb/wb/ab)操作数据,保留文件原始字节信息。
1.1.1 核心模式说明
| 模式 | 功能 | 适用场景 |
|---|---|---|
| rb | 二进制读模式 | 读取图片、视频等非文本文件 |
| wb | 二进制写模式(覆盖) | 保存图片、生成二进制文件 |
| ab | 二进制追加模式 | 向二进制文件追加内容 |
1.1.2 实战案例:通用文件复制
"""
二进制文件复制:支持图片、视频、音频等所有文件类型
核心:按字节读取和写入,保留文件原始数据
"""
def copy_binary_file(src_path, dst_path):
try:
with open(src_path, "rb") as f_in, open(dst_path, "wb") as f_out:
data = f_in.read()
f_out.write(data)
print(f"文件复制成功:{src_path} → {dst_path}")
except FileNotFoundError:
print(f"错误:源文件 {src_path} 不存在")
except PermissionError:
print(f"错误:无权限访问文件")
except Exception as e:
print(f"文件复制失败:{str(e)}")
# 调用
copy_binary_file("input.jpg", "output.jpg")
1.2 大文件分块处理
文件体积过大(1GB 以上)时,一次性读取会导致内存溢出,分块处理实现逐块读取 - 逐块写入,高效复用内存。
1.2.1 实战案例:大文件分块复制
"""
大文件分块复制:适用于 GB 级文件,避免内存溢出
chunk_size:分块大小,1024*1024 = 1MB
"""
def copy_large_file(src_path, dst_path, chunk_size=1024*1024):
try:
with open(src_path, "rb") as f_in, open(dst_path, "wb") as f_out:
while True:
chunk = f_in.read(chunk_size)
if not chunk:
break
f_out.write(chunk)
print(f"大文件复制成功:{src_path} → {dst_path}")
except Exception as e:
print(f"大文件复制失败:{str(e)}")
# 调用
copy_large_file("large_video.mp4", "copy_video.mp4")
1.2.2 分块大小选型建议
| 文件大小 | 推荐分块大小 | 内存占用 |
|---|---|---|
| 100MB 以内 | 1MB | 约 1MB |
| 1GB~5GB | 4MB~8MB | 约 4MB~8MB |
| 5GB 以上 | 16MB | 约 16MB |
1.3 路径处理与目录遍历
通过 os 模块实现路径动态拼接、目录遍历,适配 Windows/Linux/Mac 系统。
1.3.1 路径函数
| 函数 | 功能 |
|---|---|
| os.path.join() | 动态拼接路径 |
| os.path.exists() | 判断路径是否存在 |
| os.makedirs() | 创建目录 |
| os.walk() | 递归遍历目录 |
1.3.2 实战案例 1:动态路径与目录创建
import os
base_dir = "project_data"
log_dir = os.path.join(base_dir, "logs")
data_file = os.path.join(base_dir, "user_data.txt")
if not os.path.exists(log_dir):
os.makedirs(log_dir)
print(f"目录创建成功:{log_dir}")
with open(data_file, "w", encoding="utf-8") as f:
f.write("用户数据:动态路径测试")
1.3.3 实战案例 2:递归查找 .py 文件
import os
def find_py_files(root_dir):
py_files = []
for root, dirs, files in os.walk(root_dir):
for file in files:
if file.endswith(".py"):
full_path = os.path.join(root, file)
py_files.append(full_path)
return py_files
# 调用
py_list = find_py_files(".")
for file in py_list:
print(file)
2 异常处理进阶
聚焦 自定义异常、多异常精准捕获、异常链追溯,适配复杂业务错误处理。
2.1 自定义异常类
继承 Exception 实现业务专属异常,提升错误信息可读性。
2.1.1 定义自定义异常
class BusinessError(Exception):
"""业务逻辑异常"""
def __init__(self, code, message):
self.code = code
self.message = message
super().__init__(f"错误码:{code},错误信息:{message}")
class DataFormatError(BusinessError):
"""数据格式异常"""
def __init__(self, field):
super().__init__(400, f"数据格式错误:字段 {field} 不合法")
2.1.2 抛出与捕获
def process_user_data(data):
if not data:
raise BusinessError(400, "用户数据不能为空")
if "name" not in data:
raise DataFormatError("name")
return f"处理成功:{data['name']}"
try:
process_user_data({"age": 18})
except DataFormatError as e:
print(f"数据异常:{e.message}")
except BusinessError as e:
print(f"业务异常:{e.message}")
2.2 多异常精准捕获
遵循 从具体到通用 原则,避免异常覆盖。
try:
data = int(input("请输入数字:"))
result = 10 / data
except ValueError:
print("错误:请输入有效整数")
except ZeroDivisionError:
print("错误:除数不能为零")
except Exception as e:
print(f"未知错误:{e}")
else:
print(f"结果:{result}")
finally:
print("执行完毕")
2.3 异常链处理(Python 3.11+)
raise ... from ... 保留异常上下文,快速定位根源。
def read_config(file_path):
try:
with open(file_path, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError as e:
e.add_note(f"路径:{file_path}")
raise BusinessError(500, "配置文件读取失败") from e
3 模块化开发进阶
核心:高内聚、低耦合,解决大型项目模块混乱、依赖冲突问题。
3.1 包的导入优化
通过 __init__.py 控制导入行为,简化调用。
3.1.1 init.py 配置
# my_package/__init__.py
from .module1 import func1, Class1
from .module2 import func2
from .utils.helper import format_data
__all__ = ["func1", "Class1", "func2", "format_data"]
PACKAGE_VERSION = "1.0.0"
3.1.2 简化导入效果
# 优化后
from my_package import func1, format_data
3.2 绝对导入与相对导入
| 导入方式 | 优点 | 适用场景 |
|---|---|---|
| 绝对导入 | 路径清晰、跨包支持 | 大型多包项目 |
| 相对导入 | 无需硬编码包名 | 单包内部交互 |
# 绝对导入
from package_a.module_a import func_a
# 相对导入
from .module1 import func1
from ..module1 import func1
3.3 模块依赖管理
使用 requirements.txt 管理第三方依赖。
# 导出依赖
pip freeze > requirements.txt
# 安装依赖
pip install -r requirements.txt
3.4 跨文件数据共享
优先使用配置模块 / 配置类,避免全局变量。
3.4.1 配置模块(config.py)
APP_NAME = "实战项目"
DB_CONFIG = {"host": "127.0.0.1", "port": 3306}
3.4.2 配置类(面向对象)
class AppConfig:
def __init__(self):
self.app_name = "实战项目"
self.debug = True
config = AppConfig()
4 综合实战:文件处理工具包
4.1 项目结构
file_toolkit/
├── __init__.py
├── file_operate.py
├── path_utils.py
├── config_reader.py
├── logger.py
└── config.json
4.2 核心模块代码
4.2.1 日志模块(logger.py)
import logging, os
from datetime import datetime
def setup_logger():
log_dir = "logs"
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, f"{datetime.now():%Y-%m-%d}.log")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger("file_toolkit")
logger.addHandler(logging.FileHandler(log_file, encoding="utf-8"))
logger.addHandler(logging.StreamHandler())
return logger
logger = setup_logger()
4.2.2 文件操作模块(file_operate.py)
import os
from .logger import logger
def copy_file(src_path, dst_path, chunk_size=1024*1024):
try:
with open(src_path, "rb") as f_in, open(dst_path, "wb") as f_out:
while chunk := f_in.read(chunk_size):
f_out.write(chunk)
logger.info(f"复制成功:{src_path}")
return True
except Exception as e:
logger.error(f"复制失败:{e}")
return False
4.2.3 路径工具模块(path_utils.py)
import os
from .logger import logger
def find_files_by_suffix(root_dir, suffix_list):
match_files = []
for root, _, files in os.walk(root_dir):
for f in files:
if f.endswith(tuple(suffix_list)):
match_files.append(os.path.join(root, f))
return match_files
4.2.4 配置读取模块(config_reader.py)
import json, os
from .logger import logger
def read_json_config(file_path):
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
4.2.5 包初始化(init.py)
from .file_operate import copy_file
from .path_utils import find_files_by_suffix
from .config_reader import read_json_config
from .logger import logger
__version__ = "1.0.0"
__all__ = ["copy_file", "find_files_by_suffix", "read_json_config", "logger"]
4.3 项目使用示例
from file_toolkit import copy_file, find_files_by_suffix, read_json_config
copy_file("large.mp4", "backup/large.mp4")
files = find_files_by_suffix(".", [".py", ".json"])
config = read_json_config("config.json")