

📁 说明 :文件和目录操作是绝大多数 Python 应用的基础功能------无论是读取配置、保存日志,还是批量处理数据。本篇深入讲解
pathlib、os、shutil、glob等模块的高级用法 ,并强调安全性、健壮性与跨平台兼容性,助你写出专业级的文件处理代码。
你将掌握:
- 用
pathlib实现现代、面向对象的路径操作 - 安全创建/删除目录(避免
FileExistsError和空目录残留) - 高效遍历大型目录(使用生成器避免内存爆炸)
- 批量重命名、移动、复制文件
- 处理权限、元数据与临时文件
- 防范路径遍历等安全风险
1. 为什么推荐 pathlib?(vs os.path)
| 特性 | os.path |
pathlib.Path |
|---|---|---|
| 风格 | 函数式(os.path.join("a", "b")) |
面向对象(Path("a") / "b") |
| 可读性 | 较低 | 高(支持运算符) |
| 跨平台 | 需注意分隔符 | 自动处理 |
| 方法丰富度 | 基础 | 内置 .read_text(), .mkdir() 等 |
✅ 从 Python 3.4 起,官方推荐使用 pathlib
2. 核心操作实战
2.1 创建目录(安全且递归)
python
from pathlib import Path
# 创建单层目录(存在则报错)
Path("logs").mkdir(exist_ok=True)
# 创建多层目录(如 a/b/c)
Path("data/raw/images").mkdir(parents=True, exist_ok=True)
🔑 参数说明:
parents=True:自动创建父目录exist_ok=True:目录已存在时不报错
2.2 安全写入文件(避免覆盖/损坏)
方法1:原子写入(推荐)
python
from pathlib import Path
import tempfile
def safe_write(file_path: Path, content: str):
"""先写临时文件,再原子替换原文件"""
with tempfile.NamedTemporaryFile(
dir=file_path.parent,
delete=False,
mode="w",
encoding="utf-8"
) as tmp:
tmp.write(content)
tmp_path = Path(tmp.name)
# 原子替换(POSIX 系统保证)
tmp_path.replace(file_path)
✅ 优势:
- 写入失败不破坏原文件
- 避免程序崩溃导致半写入文件
方法2:简单覆盖(适合非关键数据)
python
Path("config.json").write_text('{"debug": true}', encoding="utf-8")
2.3 遍历目录(高效 & 可控)
场景1:仅当前目录下的文件
python
for item in Path("docs").iterdir():
if item.is_file():
print(item.name)
场景2:递归遍历所有 .txt 文件
python
# 使用 glob(简洁)
for txt_file in Path("project").rglob("*.txt"):
print(txt_file)
# 使用 walk(更灵活)
for root, dirs, files in Path("data").walk(): # Python 3.12+
for f in files:
if f.endswith(".log"):
print(root / f)
⚠️ 注意:
rglob()返回生成器,内存友好- 旧版本 Python(❤️.12)用
os.walk()+Path
2.4 批量操作:重命名、移动、删除
示例:为图片添加前缀
python
from pathlib import Path
img_dir = Path("photos")
for i, img in enumerate(img_dir.glob("*.jpg"), start=1):
new_name = f"vacation_{i:03d}.jpg"
img.rename(img_dir / new_name)
移动文件到归档目录
python
archive = Path("archive")
archive.mkdir(exist_ok=True)
for log in Path("logs").glob("*.log"):
log.replace(archive / log.name) # 移动(同盘)或复制+删除(跨盘)
安全删除目录(非空)
python
import shutil
# 删除整个目录树
shutil.rmtree("temp_data", ignore_errors=True)
# 或用 pathlib(Python 3.12+)
Path("temp_data").rmtree(ignore_errors=True)
❌ 不要用
os.rmdir():只能删空目录
3. 获取文件元数据
python
from pathlib import Path
import datetime
p = Path("report.pdf")
if p.exists():
stat = p.stat()
print(f"大小: {stat.st_size} 字节")
mtime = datetime.datetime.fromtimestamp(stat.st_mtime)
print(f"修改时间: {mtime}")
# 检查权限(Unix-like 系统)
print(f"可读: {os.access(p, os.R_OK)}")
💡 提示:
st_mtime= 修改时间st_ctime= 创建时间(Windows)/ 状态变更时间(Unix)
4. 安全性:防范路径遍历攻击
当处理用户输入的文件名时,必须校验路径合法性!
危险示例(切勿模仿!)
python
# 用户输入 filename = "../../../etc/passwd"
filename = input("输入文件名: ")
Path("uploads") / filename # 可能逃出 uploads 目录!
安全做法:限制在指定目录内
python
def safe_join(base_dir: Path, user_path: str) -> Path:
"""确保 user_path 不会逃出 base_dir"""
resolved = (base_dir / user_path).resolve()
if not resolved.is_relative_to(base_dir.resolve()):
raise ValueError("非法路径!")
return resolved
# 使用
try:
safe_path = safe_join(Path("uploads"), "../../secret.txt")
except ValueError as e:
print(e) # 非法路径!
🔒 关键函数:
Path.resolve():解析绝对路径(含..)Path.is_relative_to():检查是否在指定目录下(Python 3.9+)
5. 临时文件与目录(回顾 + 进阶)
自动清理的临时工作区
python
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as tmp_dir:
work_path = Path(tmp_dir)
# 在此目录下进行复杂操作
(work_path / "input.csv").write_text("...")
process_data(work_path)
# 退出 with 块后自动删除整个目录
✅ 适用场景:
- 解压 ZIP 后处理文件
- 生成中间报告
- 测试隔离环境
6. 常见陷阱与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
FileNotFoundError |
父目录不存在 | 用 mkdir(parents=True) |
| 中文文件名乱码 | 编码未指定 | open(..., encoding="utf-8") |
| 权限拒绝 | 无写权限 | 检查 os.access() 或捕获 PermissionError |
| 跨平台路径错误 | 硬编码 \ 或 / |
始终用 Path 或 os.path.join |
| 删除非空目录失败 | 用了 os.rmdir |
用 shutil.rmtree |
7. 实战:构建一个"日志归档工具"
整合所学知识,编写一个实用小工具:
python
#!/usr/bin/env python3
from pathlib import Path
import shutil
from datetime import datetime
def archive_logs(log_dir: Path, archive_dir: Path):
"""将 7 天前的日志移至归档目录"""
archive_dir.mkdir(parents=True, exist_ok=True)
now = datetime.now()
for log_file in log_dir.glob("app_*.log"):
stat = log_file.stat()
mtime = datetime.fromtimestamp(stat.st_mtime)
days_old = (now - mtime).days
if days_old >= 7:
# 构建带日期的归档名
date_str = mtime.strftime("%Y%m%d")
stem = log_file.stem
ext = log_file.suffix
new_name = f"{stem}_{date_str}{ext}"
target = archive_dir / new_name
log_file.replace(target)
print(f"归档: {log_file.name} → {new_name}")
if __name__ == "__main__":
archive_logs(Path("logs"), Path("logs/archive"))
✨ 功能亮点:
- 自动创建归档目录
- 按修改时间判断是否过期
- 重命名包含日期,避免冲突
8. 总结:文件操作黄金法则
- 用
pathlib.Path代替字符串拼接路径 - 写入关键文件时使用原子操作
- 遍历大目录用生成器(
glob,iterdir) - 处理用户输入路径时严格校验
- 删除目录用
shutil.rmtree - 始终指定文本编码(
utf-8)
🐍 记住 :
"文件系统是不可靠的外部世界,你的代码必须做好防御。"
------ 专业开发者的基本素养
下一步练习
- 为你的 To-Do List 添加"自动备份"功能 (每天备份
tasks.json到backup/) - 编写一个"清理下载文件夹"脚本(按扩展名分类移动文件)
- 实现一个安全的文件上传接口(校验路径、限制大小、扫描内容)
🧹 掌控文件系统,就是掌控数据的命脉。
用稳健的代码,守护你的每一份数据!
继续精进,成为文件操作的 Python 高手!

