目录
- Python路径操作全解析:os.path、glob与pathlib从入门到精通
-
- 一、路径操作的必要性与背景
- [二、os.path 模块:传统路径处理](#二、os.path 模块:传统路径处理)
-
- [1. 设计哲学](#1. 设计哲学)
- [2. 核心函数详解](#2. 核心函数详解)
-
- [(1) 路径拼接](#(1) 路径拼接)
- [(2) 路径信息获取](#(2) 路径信息获取)
- [(3) 路径类型判断](#(3) 路径类型判断)
- [(4) 路径解析](#(4) 路径解析)
- [3. os.path 的局限性](#3. os.path 的局限性)
- [三、glob 模块:路径模式匹配](#三、glob 模块:路径模式匹配)
-
- [1. 基本用法](#1. 基本用法)
- [2. glob 的高级模式匹配](#2. glob 的高级模式匹配)
- [3. glob 的参数](#3. glob 的参数)
- [4. glob 的应用场景](#4. glob 的应用场景)
- [四、pathlib 模块:现代路径处理](#四、pathlib 模块:现代路径处理)
-
- [1. 设计哲学](#1. 设计哲学)
- [2. 基本用法](#2. 基本用法)
-
- [(1) 创建Path对象](#(1) 创建Path对象)
- [(2) 路径操作](#(2) 路径操作)
- [(3) 遍历目录](#(3) 遍历目录)
- [(4) 文件操作](#(4) 文件操作)
- [(5) 路径解析与规范化](#(5) 路径解析与规范化)
- [(6) 文件权限操作](#(6) 文件权限操作)
- [(7) 递归遍历与文件操作](#(7) 递归遍历与文件操作)
- [(8) 路径安全处理](#(8) 路径安全处理)
- [3. pathlib 与 os.path 对比](#3. pathlib 与 os.path 对比)
- 五、补充其他路径相关模块
- 六、各个方法之间的对比
-
- [1. 路径操作的演进](#1. 路径操作的演进)
- [2. 模块对比表](#2. 模块对比表)
- [3. 选择建议](#3. 选择建议)
- [4. 代码风格对比](#4. 代码风格对比)
- [5. EAFP vs LBYL 风格](#5. EAFP vs LBYL 风格)
- 七、高级技巧与实战案例
-
- [1. 批量修改文件扩展名](#1. 批量修改文件扩展名)
- [2. 构建目录树统计器](#2. 构建目录树统计器)
- [3. 路径安全处理](#3. 路径安全处理)
- [4. 跨平台路径处理](#4. 跨平台路径处理)
- [5. 文件处理工作流](#5. 文件处理工作流)
- [6. 安全路径操作函数](#6. 安全路径操作函数)
- 八、常见问题与解决
-
- [1. 为什么在Windows上使用pathlib时路径显示为反斜杠?](#1. 为什么在Windows上使用pathlib时路径显示为反斜杠?)
- [2. 如何处理路径中的空格和特殊字符?](#2. 如何处理路径中的空格和特殊字符?)
- [3. 如何在不同系统间共享路径?](#3. 如何在不同系统间共享路径?)
- 九、总结与建议
Python路径操作全解析:os.path、glob与pathlib从入门到精通
一、路径操作的必要性与背景
在文件系统操作中,路径处理是基础且关键的环节。不同操作系统使用不同的路径分隔符(Windows用\
,Unix-like系统用/
),手动处理路径容易导致代码不可移植。Python提供了多个模块来解决这个问题,其中最常用的是os.path
、glob
和pathlib
。
二、os.path 模块:传统路径处理
1. 设计哲学
- 过程式路径处理:基于字符串的函数集合
- 典型特征:需要组合多个函数完成复杂操作,路径作为普通字符串处理
2. 核心函数详解
(1) 路径拼接
python
import os
# 传统方式
path = os.path.join("/home/user", "documents", "file.txt")
# 结果:/home/user/documents/file.txt
(2) 路径信息获取
python
# 获取最后修改时间
mtime = os.path.getmtime("file.txt") # 返回纪元秒数
# 获取文件大小
size = os.path.getsize("file.txt") # 返回字节数
# 获取路径的ctime
ctime = os.path.getctime("file.txt") # Unix上是元数据最后修改时间,Windows上是创建时间
(3) 路径类型判断
python
# 判断是否是绝对路径
is_abs = os.path.isabs("/home/user") # True
# 判断是否是文件
is_file = os.path.isfile("/home/user/file.txt") # True/False
# 判断是否是目录
is_dir = os.path.isdir("/home/user") # True/False
# 判断是否是符号链接
is_link = os.path.islink("/home/user/link") # True/False
# 判断是否是挂载点
is_mount = os.path.ismount("/home") # True/False
(4) 路径解析
python
# 分离路径的目录和文件名
directory, filename = os.path.split("/home/user/file.txt")
# directory: /home/user, filename: file.txt
# 获取文件名
filename = os.path.basename("/home/user/file.txt") # file.txt
# 获取目录名
dirname = os.path.dirname("/home/user/file.txt") # /home/user
# 分离文件名和扩展名
name, ext = os.path.splitext("report.txt")
# name: report, ext: .txt
3. os.path 的局限性
- 函数式风格:需要频繁调用多个函数,代码零散
- 字符串处理:路径作为字符串处理,容易出错
- 平台差异:需要手动处理路径分隔符差异
- 状态无感知:无状态操作,不跟踪当前工作目录
三、glob 模块:路径模式匹配
1. 基本用法
python
import glob
# 查找所有txt文件
txt_files = glob.glob("*.txt")
# 结果:['file1.txt', 'file2.txt', ...]
# 查找所有子目录下的txt文件
all_txt_files = glob.glob("**/*.txt", recursive=True)
2. glob 的高级模式匹配
python
import glob
# 匹配以数字开头的文件
num_files = glob.glob("[0-9]*.txt")
# 匹配特定扩展名
pdf_files = glob.glob("*.pdf")
# 匹配多个模式
files = glob.glob("*.txt|*.csv", recursive=True) # 仅在Python 3.10+支持
3. glob 的参数
python
# GLOB_MARK: 在每个返回的项目中加一个斜线
files = glob.glob("*.txt", flags=glob.GLOB_MARK)
# GLOB_NOSORT: 按照文件在目录中出现的原始顺序返回
files = glob.glob("*.txt", flags=glob.GLOB_NOSORT)
# GLOB_NOCHECK: 如果没有文件匹配则返回用于搜索的模式
files = glob.glob("*.nonexistent", flags=glob.GLOB_NOCHECK)
# GLOB_ONLYDIR: 仅返回与模式匹配的目录项
dirs = glob.glob("*.dir", flags=glob.GLOB_ONLYDIR)
4. glob 的应用场景
- 遍历目录中的特定类型文件
- 检索符合特定命名模式的文件
- 与os.path结合进行高级路径处理
四、pathlib 模块:现代路径处理
1. 设计哲学
- 面向对象路径操作:路径即对象(Path实例)
- 核心优势 :
- 支持链式方法调用
- 自动处理平台差异
- 集成文件系统操作
2. 基本用法
(1) 创建Path对象
python
from pathlib import Path
# 从字符串创建
file_path = Path("data/report.txt")
# 从其他Path对象创建
base_path = Path("/home/user")
data_path = base_path / "data" # /home/user/data
# 从当前工作目录创建
cwd = Path.cwd() # 当前工作目录
# 从主目录创建
home = Path.home() # 主目录
(2) 路径操作
python
# 路径拼接(使用/运算符)
path = Path("/home") / "user" / "data" / "file.txt"
# 获取文件名
filename = path.name # file.txt
# 获取扩展名
ext = path.suffix # .txt
# 获取父目录
parent = path.parent # /home/user/data
# 获取文件大小
size = path.stat().st_size
# 获取最后修改时间
mtime = path.stat().st_mtime
# 判断文件是否存在
exists = path.exists()
# 判断是否是文件
is_file = path.is_file()
# 判断是否是目录
is_dir = path.is_dir()
(3) 遍历目录
python
# 查找当前目录下所有txt文件
txt_files = list(Path.cwd().glob("*.txt"))
# 查找所有子目录下的txt文件
all_txt_files = list(Path.cwd().rglob("*.txt"))
(4) 文件操作
python
# 读取文件内容
content = Path("file.txt").read_text(encoding="utf-8")
# 写入文件
Path("new_file.txt").write_text("Hello, World!", encoding="utf-8")
# 创建目录
Path("new_dir").mkdir(exist_ok=True)
# 重命名文件
Path("old.txt").rename("new.txt")
# 删除文件
Path("file.txt").unlink()
(5) 路径解析与规范化
python
from pathlib import Path
# 获取绝对路径
absolute_path = Path("relative/path").resolve()
# 处理符号链接
resolved_path = Path("symlink").resolve()
# 获取相对于某个路径的相对路径
relative_path = Path("current_dir/file.txt").relative_to(Path("current_dir"))
(6) 文件权限操作
python
from pathlib import Path
p = Path("file.txt")
# 获取文件权限
permissions = p.stat().st_mode
# 更改文件权限
p.chmod(0o644) # 读写权限给用户,读权限给组和其他用户
# 获取文件所有者
owner = p.owner()
group = p.group()
(7) 递归遍历与文件操作
python
from pathlib import Path
# 递归遍历所有文件
for file in Path.cwd().rglob("*"):
if file.is_file():
print(f"文件: {file}")
else:
print(f"目录: {file}")
# 读取所有文本文件
text_files = [f for f in Path.cwd().rglob("*.txt") if f.is_file()]
for file in text_files:
print(file.read_text(encoding="utf-8"))
(8) 路径安全处理
python
from pathlib import Path
def safe_path(path: str) -> Path:
"""确保路径是安全的,不包含恶意路径"""
p = Path(path).resolve()
# 确保路径在安全目录内
if not p.is_relative_to(Path.cwd()):
raise ValueError("路径超出安全范围")
return p
# 示例
try:
safe_file = safe_path("../malicious/file.txt")
except ValueError as e:
print(f"安全错误: {e}")
3. pathlib 与 os.path 对比
功能描述 | os.path 方法 | pathlib 方法 |
---|---|---|
路径拼接 | os.path.join() | / 运算符或 .joinpath() |
获取文件名 | os.path.basename() | .name 属性 |
获取父目录 | os.path.dirname() | .parent 属性 |
获取扩展名 | os.path.splitext()[1] | .suffix 属性 |
判断文件存在 | os.path.exists() | .exists() 方法 |
判断是否为文件 | os.path.isfile() | .is_file() 方法 |
判断是否为目录 | os.path.isdir() | .is_dir() 方法 |
获取绝对路径 | os.path.abspath() | .resolve() 方法 |
路径规范化 | os.path.normpath() | .resolve(strict=False) |
相对路径转换 | os.path.relpath() | .relative_to() 方法 |
获取文件大小 | os.path.getsize() | .stat().st_size |
获取最后修改时间 | os.path.getmtime() | .stat().st_mtime |
创建目录 | os.mkdir() | .mkdir(parents=True) |
删除文件 | os.remove() | .unlink() |
删除空目录 | os.rmdir() | .rmdir() |
五、补充其他路径相关模块
1. shutil 模块:高级文件操作
设计哲学
- 高阶文件操作:提供比os更高层次的文件操作函数
- 核心优势:简化文件复制、移动、归档等复杂操作
常用函数详解
python
import shutil
from pathlib import Path
# 复制文件(保留元数据)
shutil.copy("source.txt", "destination.txt")
# 复制文件(保留所有元数据,包括时间戳)
shutil.copy2("source.txt", "destination2.txt")
# 递归复制目录
shutil.copytree("source_dir", "destination_dir")
# 移动/重命名文件或目录
shutil.move("old_name.txt", "new_name.txt")
# 递归删除目录(包括其内容)
shutil.rmtree("directory_to_delete")
# 创建归档文件(如zip、tar等)
shutil.make_archive("archive", "zip", "directory_to_compress")
# 解压归档文件
shutil.unpack_archive("archive.zip", "extract_dir")
实战案例:备份目录
python
def backup_directory(src_dir, backup_dir):
"""创建目录的备份"""
backup_path = Path(backup_dir) / Path(src_dir).name
if backup_path.exists():
shutil.rmtree(backup_path)
shutil.copytree(src_dir, backup_path)
print(f"已备份到: {backup_path}")
2. tempfile 模块:安全临时文件操作
设计哲学
- 安全创建临时文件:避免与其他进程的临时文件冲突
- 自动清理:默认在文件对象关闭后自动删除
常用函数详解
python
import tempfile
import os
# 创建临时文件(不自动删除)
with tempfile.NamedTemporaryFile(delete=False) as tmp:
tmp.write(b"Temporary content")
temp_path = tmp.name
# 读取临时文件
with open(temp_path, 'r') as f:
print(f.read())
# 删除临时文件
os.unlink(temp_path)
# 创建临时目录
temp_dir = tempfile.mkdtemp()
print(f"临时目录: {temp_dir}")
# 使用后删除
shutil.rmtree(temp_dir)
# 临时文件对象(自动删除)
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(b"Auto-deleted content")
print("临时文件已创建,将在退出时自动删除")
3. fnmatch 模块:文件名模式匹配
设计哲学
- 轻量级文件名匹配:专注于单个文件名的匹配,比glob更轻量
- 与glob相似但更简单:适合不需要递归匹配的场景
常用函数详解
python
import fnmatch
# 检查文件名是否匹配模式(不区分大小写)
print(fnmatch.fnmatch("report.pdf", "*.pdf")) # True
print(fnmatch.fnmatch("Report.PDF", "*.pdf")) # True (默认不区分大小写)
# 区分大小写匹配
print(fnmatch.fnmatchcase("Report.PDF", "*.pdf")) # False
# 遍历目录匹配文件
for file in os.listdir("."):
if fnmatch.fnmatch(file, "*.txt"):
print(f"匹配的文本文件: {file}")
实战案例:过滤文件列表
python
def filter_files(file_list, pattern):
"""过滤文件列表,返回匹配模式的文件"""
return [f for f in file_list if fnmatch.fnmatch(f, pattern)]
六、各个方法之间的对比
1. 路径操作的演进
- 传统方式:字符串拼接(不推荐)
- 过渡方式:os.path(字符串处理,功能有限)
- 现代方式:pathlib(面向对象,代码简洁,跨平台)
- 高级操作:shutil(高级文件操作)
- 模式匹配:glob(高效文件匹配)
- 安全操作:tempfile(安全临时文件)
2. 模块对比表
模块/库 | 适用场景 | 优点 | 缺点 | 推荐度 |
---|---|---|---|---|
os |
基础系统交互 | 通用,广泛使用 | 代码不直观 | ★★★☆ |
os.path |
路径字符串处理 | 与os配合 | 仍然基于字符串 | ★★☆ |
pathlib |
现代路径操作 | 面向对象,代码简洁,跨平台 | 需要Python 3.4+ | ★★★★ |
glob |
模式匹配文件 | 简单高效的文件匹配 | 仅用于匹配 | ★★★★ |
shutil |
高级文件操作 | 简化复制、移动、归档 | 仅用于操作 | ★★★★ |
tempfile |
临时文件 | 安全创建临时文件 | 仅用于临时文件 | ★★★☆ |
fnmatch |
文件名匹配 | 轻量级匹配 | 仅用于单个文件名 | ★★★ |
3. 选择建议
场景 | 推荐工具 | 说明 |
---|---|---|
基础路径处理 | pathlib | 代码简洁,跨平台 |
文件复制/移动 | shutil | 简化高级文件操作 |
文件模式匹配 | glob | 高效查找匹配文件 |
临时文件 | tempfile | 安全创建临时文件 |
文件名匹配 | fnmatch | 轻量级匹配,适合单个文件 |
旧代码维护 | os.path | 保持与现有代码一致 |
4. 代码风格对比
os.path 风格 (传统)
python
import os
file_path = os.path.join(os.path.dirname(__file__), "data", "report.txt")
if os.path.exists(file_path) and os.path.isfile(file_path):
with open(file_path, 'r') as f:
content = f.read()
pathlib 风格 (现代)
python
from pathlib import Path
file_path = Path(__file__).parent / "data" / "report.txt"
if file_path.exists() and file_path.is_file():
content = file_path.read_text(encoding="utf-8")
5. EAFP vs LBYL 风格
EAFP (Easier to Ask for Forgiveness than Permission)
python
from pathlib import Path
try:
content = Path("file.txt").read_text()
print("文件存在且可读取")
except FileNotFoundError:
print("文件不存在")
except PermissionError:
print("没有读取权限")
LBYL (Look Before You Leap)
python
from pathlib import Path
path = Path("file.txt")
if path.exists() and path.is_file() and path.is_readable():
content = path.read_text()
else:
print("文件不存在或不可读")
七、高级技巧与实战案例
1. 批量修改文件扩展名
python
# 使用pathlib
from pathlib import Path
for p in Path("reports").glob("*.txt"):
p.rename(p.with_suffix(".md"))
# 使用os.path
import os
for root, _, files in os.walk("reports"):
for f in files:
if f.endswith(".txt"):
old_path = os.path.join(root, f)
new_path = os.path.splitext(old_path)[0] + ".md"
os.rename(old_path, new_path)
2. 构建目录树统计器
python
from pathlib import Path
def analyze_dir(path: Path):
return {
"dir_count": sum(1 for _ in path.rglob("*/")),
"file_count": sum(1 for _ in path.rglob("*") if _.is_file())
}
result = analyze_dir(Path.cwd())
print(f"目录数: {result['dir_count']}, 文件数: {result['file_count']}")
3. 路径安全处理
python
from pathlib import Path
def safe_path(path: str) -> Path:
"""确保路径是安全的,不包含恶意路径"""
p = Path(path).resolve()
if not p.is_absolute():
p = p.resolve()
return p
# 示例
safe_file = safe_path("../malicious/file.txt")
print(safe_file) # 将转换为绝对路径,避免目录遍历攻击
4. 跨平台路径处理
python
from pathlib import Path
# 跨平台路径拼接
path = Path("data") / "images" / "photo.jpg"
# 获取平台特定的分隔符
separator = Path.sep # '/' on Unix, '\' on Windows
# 转换为字符串(自动处理平台差异)
str_path = str(path) # 在Windows上会是"data\images\photo.jpg"
5. 文件处理工作流
python
from pathlib import Path
import shutil
import glob
def process_files(source_dir, destination_dir, pattern="*.txt"):
"""处理匹配模式的文件,复制到目标目录"""
source = Path(source_dir)
dest = Path(destination_dir)
# 创建目标目录
dest.mkdir(parents=True, exist_ok=True)
# 处理所有匹配的文件
for file in source.glob(pattern):
if file.is_file():
# 创建安全的文件名
safe_name = file.name.replace(" ", "_")
dest_file = dest / safe_name
# 复制文件(保留元数据)
shutil.copy2(file, dest_file)
# 添加处理日志
print(f"已处理: {file} -> {dest_file}")
print(f"共处理 {len(list(source.glob(pattern)))} 个文件")
# 使用示例
process_files("data/source", "data/processed", "*.txt")
6. 安全路径操作函数
python
from pathlib import Path
import os
def safe_file_operation(file_path, operation, *args, **kwargs):
"""安全执行文件操作,防止路径遍历攻击"""
# 获取当前工作目录的绝对路径
base_path = Path.cwd().resolve()
# 解析目标路径并检查是否在安全范围内
target_path = Path(file_path).resolve()
if not target_path.is_relative_to(base_path):
raise ValueError("路径超出安全范围,可能包含目录遍历攻击")
# 执行操作
try:
return operation(target_path, *args, **kwargs)
except FileNotFoundError as e:
print(f"文件未找到: {file_path}")
raise e
except PermissionError as e:
print(f"权限错误: {file_path}")
raise e
# 使用示例
def read_file_safe(file_path):
return safe_file_operation(file_path, Path.read_text)
content = read_file_safe("data/file.txt")
八、常见问题与解决
1. 为什么在Windows上使用pathlib时路径显示为反斜杠?
- 在Python字符串中,反斜杠是转义字符
- 解决方案:使用原始字符串或正斜杠
python
# 正确方式
p = Path(r"C:\Users\user") # 原始字符串
p = Path("C:/Users/user") # 正斜杠
# 错误方式
p = Path("C:\Users\user") # 会被解释为C:Usersuser
2. 如何处理路径中的空格和特殊字符?
- pathlib会自动处理特殊字符,无需额外处理
python
p = Path("my folder/file with spaces.txt")
# 无需额外转义,pathlib会正确处理
3. 如何在不同系统间共享路径?
- 使用pathlib的
as_posix()
方法
python
path = Path("data/file.txt")
posix_path = path.as_posix() # 'data/file.txt' (Unix风格)
windows_path = path.as_posix().replace("/", "\\") # 'data\file.txt' (Windows风格)
九、总结与建议
- 新项目首选pathlib:它是Python 3.4+的标准库,提供了面向对象的路径处理方式,代码更简洁、可读性更好。
- 避免硬编码路径分隔符:使用os.path.join()或pathlib的/运算符,避免使用硬编码的'/'或''。
- 使用glob进行模式匹配:特别是当需要遍历特定模式的文件时,glob与pathlib结合使用效果最佳。
- 处理路径安全 :在处理用户输入的路径时,使用
.resolve()
确保路径的安全性,避免目录遍历攻击。 - 考虑EAFP风格:Python社区推荐EAFP(先尝试,再捕获异常)风格,比LBYL(先检查,再操作)更简洁。
- 逐步迁移:如果维护旧代码,可以逐步将os.path代码迁移到pathlib,提高代码质量和可维护性。