第一部分: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 文件复制常见问题
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(支持语法提示、匹配结果实时预览)
2.菜鸟工具-正则表达式测试
第二部分 正则表达式
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 转义字符(\)
当需要匹配元字符本身(如 ./*/()时,需用 \ 转义:
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 = 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:忽略大小写与换行
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")
复制代码
|