1 pathlib 库简介
pathlib 是 python 3.4 及更高版本中的标准库,它采用面向对象的方法来处理文件系统路径。与传统的 os.path 模块不同,pathlib 将路径表示为对象而不是字符串,使得路径操作更加直观和 pythonic。
1.1 为什么要使用 pathlib?
传统上,python 使用 os.path 模块处理路径,它将路径作为字符串处理,这种方式存在一些问题:
- 操作分散在不同模块(os、os.path、glob、shutil 等)
- 代码跨平台兼容性差
- 路径操作不够直观
pathlib 解决了这些问题,它提供统一的 api,支持跨平台路径处理,并且代码更易读。
2 基本操作介绍
2.1 路径操作
2.1.1 导入和创建 path 对象
from pathlib import path
# 创建path对象的几种方式
current_path = path() # 当前目录
relative_path = path('folder/subfolder') # 相对路径
absolute_path = path('/home/user/documents') # 绝对路径(linux/mac)
windows_path = path('c:/users/name/documents') # 绝对路径(windows)
# 使用类方法
current_dir = path.cwd() # 当前工作目录
home_dir = path.home() # 用户主目录
2.1.2 路径拼接和解析
pathlib 使用斜杠运算符 / 来拼接路径,这比传统的 os.path.join() 更直观:
# 路径拼接
base_dir = path('/base/demo')
sub_path = base_dir / 'subfolder' / 'file.txt'
print(sub_path) # 输出: /base/demo/subfolder/file.txt
# 解析路径组成部分
path = path('d:/users/base_demo/demo.py')
print(path.name) # 文件名(含后缀): demo.py
print(path.stem) # 文件名(不含后缀): demo
print(path.suffix) # 文件后缀: .py
print(path.parent) # 父目录: d:/users/base_demo
print(path.anchor) # 锚点(驱动器): d:
2.1.3 路径判断和解析
path = path('/path/to/file.txt')
# 路径判断
print(path.exists()) # 路径是否存在
print(path.is_file()) # 是否是文件
print(path.is_dir()) # 是否是目录
print(path.is_absolute()) # 是否是绝对路径
# 路径解析
print(path.resolve()) # 获取绝对路径,解析任何符号链接
print(path.as_uri()) # 将路径表示为file url
2.2 目录操作
2.2.1 创建和删除目录
# 创建目录
new_dir = path('new_directory')
new_dir.mkdir(exist_ok=true) # exist_ok=true表示目录已存在时不报错
# 创建多级目录
deep_dir = path('path/to/deep/directory')
deep_dir.mkdir(parents=true, exist_ok=true) # parents=true创建所有必要的父目录
# 删除目录(目录必须为空)
empty_dir = path('empty_directory')
empty_dir.rmdir()
2.2.2 遍历目录内容
path = path('.')
# 遍历直接子项
for item in path.iterdir():
if item.is_file():
print(f"文件: {item.name}")
elif item.is_dir():
print(f"目录: {item.name}")
# 使用glob模式匹配
for py_file in path.glob('*.py'):
print(f"python文件: {py_file}")
# 递归匹配(搜索子目录)
for py_file in path.rglob('*.py'):
print(f"python文件: {py_file}")
2.3 文件操作
2.3.1 文件读写
pathlib 提供了便捷的文件读写方法:
file_path = path('example.txt')
# 写入文件
file_path.write_text('hello, world!') # 写入文本
# 读取文件
content = file_path.read_text()
print(content)
# 二进制读写
data = b'binary data'
file_path.write_bytes(data) # 写入二进制数据
binary_content = file_path.read_bytes() # 读取二进制数据
# 使用open方法(更细粒度的控制)
with file_path.open('a') as f: # 追加模式
f.write('\nappended text')
2.3.2 文件属性和操作
file_path = path('example.txt')
# 获取文件属性
stat_info = file_path.stat()
print(f"文件大小: {stat_info.st_size} 字节")
print(f"创建时间: {stat_info.st_ctime}")
print(f"修改时间: {stat_info.st_mtime}")
# 文件操作
new_path = file_path.rename('new_name.txt') # 重命名
copied_path = new_path.replace('backup.txt') # 移动/复制(会覆盖已存在文件)
# 删除文件
file_path.unlink(missing_ok=true) # missing_ok=true表示文件不存在时不报错
2.4 汇总
| 操作类别 | 具体操作 | pathlib方法/示例 | 关键点/注意事项 |
|---|---|---|---|
| 路径操作 | 创建路径对象 | path("folder/file.txt"), path.cwd() / "subdir" / "file.txt" | 使用/运算符拼接路径,直观且跨平台。 |
| 路径操作 | 获取路径组成部分 | path.name (文件名)path.parent (父目录)path.suffix (扩展名)path.stem (无扩展名的文件名) | 这些是属性,直接调用即可。 |
| 路径操作 | 路径分解与拼接 | path.parts (分解为元组)path.joinpath("sub", "file.txt") (拼接) | parts属性便于分析路径结构。 |
| 路径操作 | 检查路径属性 | path.exists() (存在性)path.is_file() (是文件)path.is_dir() (是目录)path.is_absolute() (是绝对路径) | 在进行操作前进行检查可使代码更健壮。 |
| 路径操作 | 转换路径 | path.absolute() (绝对路径)path.resolve() (绝对路径并解析符号链接) | resolve()会消除路径中的"…"等符号。 |
| 文件操作 | 创建文件 | path.touch() | 如果文件已存在,touch会更新其修改时间。也可用于创建空文件。 |
| 文件操作 | 读取文件内容 | path.read_text(encoding="utf-8")``path.read_bytes() | 一次性读取全部内容。对于大文件,建议使用open方式逐行读取。 |
| 文件操作 | 写入文件内容 | path.write_text("content", encoding="utf-8")``path.write_bytes(data) | 会覆盖文件原有内容。若需追加,需使用open模式。 |
| 文件操作 | 复制文件 | shutil.copy(src, dst)``shutil.copy2(src, dst) | 复制文件。创建源文件的一个副本,原始文件保留。shutil.copy2复制文件内容及所有元数据(如创建时间、修改时间)。 |
| 文件操作 | 移动/重命名文件 | shutil.move(src, dst) | 移动/重命名文件或目录。原始文件在操作后会被删除。 |
| 文件操作 | 删除文件 | path.unlink(missing_ok=false) | 设置missing_ok=true时,若文件不存在则忽略错误。 |
| 目录操作 | 创建目录(单级) | path.mkdir() | 默认只能创建末级目录,若父目录不存在会报错。 |
| 目录操作 | 创建目录(多级) | path.mkdir(parents=true, exist_ok=true) | parents=true创建父目录;exist_ok=true忽略目录已存在的情况。 |
| 目录操作 | 删除目录 | path.rmdir() | 只能删除空目录。 |
| 目录操作 | 遍历目录内容 | for item in path.iterdir(): | 返回一个生成器,包含该目录下的所有文件和子目录的path对象。 |
| 目录操作 | 递归遍历与文件匹配 | for file in path.glob("*.txt"): (当前目录)for file in path.rglob("*.txt"): (递归所有子目录) | 使用通配符模式匹配文件,非常强大。 |
| 获取信息 | 获取文件状态信息 | path.stat() | 返回一个包含文件大小(st_size)、修改时间(st_mtime)等详细信息的对象。 |
3 常用场景演示
3.1 常用场景罗列
| 场景类别 | 场景描述 | 预期输出 |
|---|---|---|
| 文件夹列表获取 | 获取直接子文件夹名称 | 返回直接子文件夹的名称列表 |
| 获取所有子文件夹路径 | 返回所有子文件夹的 path 对象列表 | |
| 文件列表获取 | 获取直接子文件名称(含扩展名) | 返回直接子文件的名称列表(带后缀) |
| 获取所有子文件名称(无扩展名) | 返回所有子文件的名称列表(无后缀) | |
| 获取所有子文件路径 | 返回所有子文件的 path 对象列表 | |
| 条件筛选 | 按字符串逻辑与(and)筛选文件夹路径 | 返回满足所有筛选条件的文件夹路径列表 |
| 按字符串逻辑或(or)筛选文件路径 | 返回满足任一筛选条件的文件路径列表 | |
| 文件夹操作 | 清空目标文件夹 | 删除目标文件夹内所有内容并重建空文件夹 |
| 复制目录结构 | 在目标位置创建与源目录相同的空文件夹结构 | |
| 查找叶子目录 | 返回不包含任何子目录的最底层目录(叶子目录)列表 | |
| 文件操作 | 复制指定格式文件 | 将源目录(及子目录)中所有指定格式的文件复制到目标文件夹 |
| 复制目录和文件 | 先复制目录结构,再将指定格式的文件复制到对应目录 | |
| 复制并重命名文件 | 将文件复制到目标文件夹,并按新名称保存(保留原扩展名) |
*使用技巧与说明:
- 路径对象与字符串:获取路径列表的方法返回的是
pathlib.path对象列表,在进行字符串操作(如判断是否包含特定字符)时,常需要使用str(item)转换。若最终需要字符串路径,可以这样处理:[str(path) for path in path_list]。 - 过滤逻辑:
logical参数提供了灵活的筛选方式。例如,查找包含"报告"或"总结"的文件夹,可使用逻辑或;而查找同时包含"2024"和"月度报告"的文件夹,则需使用逻辑与。 - 递归搜索:使用
rglob('*')的方法会遍历指定路径下的所有子目录,适合需要对整个目录树进行操作的场景。
3.2 汇总成一个类+main()演示
下面是一个完整的 fileio 类,封装了常见的文件路径操作,同时对3.1罗列的场景进行了简单的示例:
from pathlib import path
import shutil
import time
class fileio:
"""
使用 pathlib 封装常见文件路径操作的实用类
该类提供了一系列用于文件和目录操作的方法,包括遍历、筛选、清空文件夹等功能。
所有路径操作均基于 pathlib 库,确保跨平台兼容性。
"""
def __init__(self, source_folder: none |str | path, target_folder:none |str | path) -> none:
"""
初始化 fileio 实例
args:
source_folder: 源目录路径,可以是字符串或 path 对象
"""
self.source_folder = path(source_folder) # 保证路径是 path 格式
self.target_folder = path(target_folder) #目标文件
if not self.target_folder.exists(): #目标文件夹不存在时,新建
self.target_folder.mkdir(parents=true, exist_ok=true)
def list_folder_names(self, recursive: bool = true) -> list[str]:
"""
获取文件夹名称列表
args:
recursive: 是否递归获取所有子文件夹,默认为 true
true - 递归获取所有子文件夹
false - 只获取直接子文件夹
returns:
文件夹名称列表
"""
folder_path = self.source_folder
folder_names = [item.name
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir()) #iterdir()比glob('*')更高效,
if item.is_dir()]
return folder_names
def list_folder_paths(self, recursive: bool = true) -> list[path]:
"""
获取文件夹路径列表
args:
recursive: 是否递归获取所有子文件夹路径
returns:
path 对象列表,包含所有匹配的文件夹路径
"""
folder_path = self.source_folder
folder_paths = [item
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir())
if item.is_dir()]
return folder_paths
def list_file_names(self, recursive: bool = true, include_extension: bool = true) -> list[str]:
"""
获取文件名列表
args:
recursive: 是否递归获取所有子文件
include_extension: 是否包含文件扩展名
true - 包含扩展名 (e.g., "document.txt")
false - 不包含扩展名 (e.g., "document")
returns:
文件名列表
"""
folder_path = self.source_folder
file_names = [
item.name if include_extension else item.stem
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir())
if item.is_file()
]
return file_names
def list_file_paths(self, recursive: bool = true) -> list[path]:
"""
获取文件路径列表
args:
recursive: 是否递归获取所有子文件路径
returns:
path 对象列表,包含所有匹配的文件路径
"""
folder_path = self.source_folder
file_paths = [item
for item in (folder_path.rglob('*') if recursive else folder_path.iterdir())
if item.is_file()]
return file_paths
@staticmethod
def filter_by_strings(items: list[str] | list[path], target_strings: list[str], logical_or: bool = true) -> list[str]:
"""
根据字符串列表筛选项目
args:
items: 要筛选的项目列表
target_strings: 目标字符串列表,用于筛选条件
logical_or: 逻辑操作类型
true - 逻辑或(包含任意一个目标字符串即匹配)
false - 逻辑与(包含所有目标字符串才匹配)
returns:
筛选后的项目列表
"""
if logical_or: # 逻辑or:包含任意一个字符串即可
filtered_items = [item
for item in items
if any(target_str in str(item) for target_str in target_strings)
]
else: # 逻辑and:包含所有字符串
filtered_items = [item
for item in items
if all(target_str in str(item) for target_str in target_strings)
]
return filtered_items
@staticmethod
def clear_folder(target_dir: str|path, max_attempts: int = 3, delay_seconds: float = 3) -> bool|none:
"""
通过删除并重建文件夹的方式来清空目标文件夹,支持重试机制
args:
max_attempts: 最大尝试次数,默认为3次
delay_seconds: 每次重试前的等待时间(秒),默认为3秒
returns:
bool: 成功清空返回 true,失败返回 false
raises:
permissionerror: 当文件被占用且无法删除时
oserror: 当发生其他操作系统错误时
"""
attempts = 0
while attempts < max_attempts:
try:
# 尝试清空文件夹的核心逻辑
source_folder = path(target_dir)
if source_folder.exists():
shutil.rmtree(source_folder) # 递归删除整个文件夹
# 短暂延迟确保删除完全生效
time.sleep(0.05)
source_folder.mkdir(exist_ok=true) # 重新创建空文件夹
print(f"成功清空文件夹: {source_folder}")
return true
except permissionerror as error:
# 捕获权限错误(通常是因为文件被占用)
attempts += 1
print(f"尝试 {attempts}/{max_attempts} 失败: 文件可能正被其他程序占用。错误信息: {error}")
if attempts < max_attempts:
print(f"等待 {delay_seconds} 秒后重试...")
time.sleep(delay_seconds)
else:
print(f"已达到最大重试次数 {max_attempts},无法清空文件夹。")
return false
except oserror as error:
# 捕获其他操作系统错误,例如路径无效等
print(f"发生操作系统错误: {error}")
return false
def copy_directory_structure(self, recursive: bool = true) -> none:
"""
复制源目录结构到目标位置(不复制文件内容)
递归复制源目录下的所有子目录结构到目标位置,但不会复制任何文件内容,
只创建空的目录结构。
args:
target_dir: 目标目录路径,可以是字符串或 path 对象
"""
source_path = self.source_folder
target_path = self.target_folder
# 确保目标根目录存在
target_path.mkdir(parents=true, exist_ok=true)
# 使用 rglob 递归遍历源目录下的所有子目录
for dir_path in (source_path.rglob('*') if recursive else source_path.glob('*')):
if dir_path.is_dir():
# 计算当前目录相对于源目录的相对路径
try:
relative_path = dir_path.relative_to(source_path)
except valueerror:
# 如果 dir_path 不是 source_path 的子路径,跳过
continue
# 构建目标目录路径并创建
target_subdir = target_path / relative_path
target_subdir.mkdir(parents=true, exist_ok=true)
print(f"创建目录: {target_subdir}")
def find_leaf_directories(self) -> list[path]:
"""
查找并返回所有叶子目录(不包含子目录的目录)
returns:
叶子目录的 path 对象列表
"""
source_path = self.source_folder
leaf_directories = []
# 识别叶子目录:没有子目录的目录就是叶子目录
for dir_path in source_path.rglob('*'):
if dir_path.is_dir():
# 检查当前目录是否包含子目录
has_subdirectories = any(item.is_dir() for item in dir_path.iterdir())
if not has_subdirectories:
leaf_directories.append(dir_path)
# 输出所有叶子目录信息
result_paths = []
print("\n所有叶子目录(最深层次的子目录):")
for leaf_path in leaf_directories:
# 计算相对于源目录的路径用于显示
try:
relative_leaf_path = leaf_path.relative_to(source_path)
result_paths.append(source_path / relative_leaf_path)
# 计算目录深度
depth = len(relative_leaf_path.parts)
print(f"- {relative_leaf_path} (深度: {depth})")
except valueerror:
# 如果路径计算出错,跳过
continue
return result_paths
#将一个文件夹里的所有指定格式文件复制到另一个目标文件夹
def copy_files(self, file_extension='.tif') -> none:
source_files = self.list_file_paths(true) #获取原始文件夹里所有的文件路径
for source_file in source_files:
if file_extension in str(source_file):
shutil.copy2(source_file, self.target_folder)#将原始文件夹里所有的文件复制到对应文件夹
print(f"已复制: {source_file} -> {target_folder}")
#将一个文件夹里的所有目录和指定格式文件复制到另一个文件夹
def copy_folders_and_files(self, file_extension='.tif') -> none:
self.copy_directory_structure(true)
source_files = self.list_file_paths(true) # 获取原始文件夹里所有的文件路径
for source_file in source_files:
if file_extension in str(source_file):
# 计算 source_file 相对于 self.source_folder 的相对路径
relative_path = source_file.relative_to(self.source_folder)
# 将相对路径拼接到目标文件夹下
target_file = self.target_folder / relative_path
shutil.copy2(source_file, target_file) # 将原始文件夹里所有的文件复制到对应文件夹
print(f"已复制: {source_file} -> {target_file}")
#将一个文件复制到目标文件夹里,并重命名
@staticmethod
def copy_rename_file(source_file_path:str|path, target_folder:str|path,new_filename:str) -> none:
extension_name = path(source_file_path).suffix
target_file_path = path(target_folder) / path(new_filename +extension_name)
shutil.copy2(source_file_path, target_file_path)
print(f"已复制: {source_file_path} -> {target_file_path}")
if __name__ == '__main__':
# # 测试路径,请替换为实际路径
source_folder = path(r"f:\source")
target_folder = path(r"f:\target")
file_io = fileio(source_folder, target_folder) # 实例化类
target_strings = [".tif", ".docx"]
#
# 测试直接子文件夹相关方法
print("=== 直接子文件夹 ===")
current_folder_names = file_io.list_folder_names(false) #获取直接文件夹名
current_folder_paths = file_io.list_folder_paths(false) #获取直接文件夹路径
filtered_current_folder_paths_and = file_io.filter_by_strings(current_folder_paths, target_strings, false) #按照逻辑与筛选含所有关键字的直接子文件夹路径
filtered_current_folder_paths_or = file_io.filter_by_strings(current_folder_paths, target_strings, true) #按照逻辑或筛选含任一关键字的直接子文件夹路径
print("直接子文件夹名称列表:", current_folder_names)
print("直接子文件夹路径列表:", current_folder_paths)
print("and条件过滤的文件夹:", filtered_current_folder_paths_and)
print("or条件过滤的文件夹:", filtered_current_folder_paths_or)
# 测试所有子文件夹相关方法
print("\n=== 所有子文件夹 ===")
all_folder_names = file_io.list_folder_names(true) #获取所有子文件夹名
all_folder_paths = file_io.list_folder_paths(true) #获取所有子文件夹路径
filtered_all_folder_paths_and = file_io.filter_by_strings(all_folder_paths, target_strings, false) #按照逻辑与筛选含所有关键字的子文件夹路径
filtered_all_folder_paths_or = file_io.filter_by_strings(all_folder_paths, target_strings, true) #按照逻辑或筛选含任一关键字的子文件夹路径
print("所有子文件夹名称:", all_folder_names)
print("所有子文件夹路径:", all_folder_paths)
print("and条件过滤的所有子文件夹:", filtered_all_folder_paths_and)
print("or条件过滤的所有子文件夹:", filtered_all_folder_paths_or )
# 测试直接子文件相关方法
print("\n=== 直接子文件 ===")
current_file_lists_with_extension = file_io.list_file_names(false,true) #获取所有直接文件的名称,有扩展名
current_file_lists_no_extension = file_io.list_file_names(false,false) #获取所有直接文件的名称,无扩展名
current_file_path_lists = file_io.list_file_paths(false) #获取直接文件路径
filtered_file_path_lists_and = file_io.filter_by_strings(current_file_path_lists, target_strings, false) #按照逻辑与筛选含所有关键字的直接子文件路径
filtered_file_path_lists_or = file_io.filter_by_strings(current_file_path_lists, target_strings, true) #按照逻辑或筛选含任一关键字的直接子文件路径
print("带扩展名的文件:", current_file_lists_with_extension)
print("不带扩展名的文件:", current_file_lists_no_extension)
print("文件路径列表:", current_file_path_lists )
print("and条件过滤的文件:", filtered_file_path_lists_and)
print("or条件过滤的文件:", filtered_file_path_lists_or)
# 测试所有子文件相关方法
print("\n=== 所有子文件 ===")
all_file_lists_with_extension = file_io.list_file_names(true,true) #获取所有文件的名称,有扩展名
all_file_lists_no_extension = file_io.list_file_names(true,false) #获取所有文件的名称,无扩展名
all_file_path_lists = file_io.list_file_paths(true) #获取所有文件路径
filtered_all_file_path_lists_and = file_io.filter_by_strings(all_file_path_lists, target_strings, false) #按照逻辑与筛选含所有关键字的所有文件路径
filtered_all_file_path_lists_or = file_io.filter_by_strings(all_file_path_lists, target_strings, true) #按照逻辑或筛选含任一关键字的所有文件路径
print("所有文件(带扩展名):", all_file_lists_with_extension)
print("所有文件(不带扩展名):", all_file_lists_no_extension)
print("所有文件路径:", all_file_path_lists)
print("and条件过滤的所有文件:", filtered_all_file_path_lists_and)
print("or条件过滤的所有文件:", filtered_all_file_path_lists_or)
#清空目标文件夹里的所有内容
file_io.clear_folder(file_io.target_folder)
#复制所有目录
file_io.copy_directory_structure(true) #true,复制所有目录,false,复制当前目录
#找到所有叶子目录
all_leaves = file_io.find_leaf_directories()
print(all_leaves)
# 清空目标文件夹里的所有内容
file_io.clear_folder(file_io.target_folder)
#复制特定格式的文件到指定目录
file_io.copy_files(".docx")
# 清空目标文件夹里的所有内容
file_io.clear_folder(file_io.target_folder)
#复制并重命名
i=0
for filtered_all_file_path_lists_or in filtered_all_file_path_lists_or:
file_io.copy_rename_file(filtered_all_file_path_lists_or,file_io.target_folder,str(i))
i = i + 1
5 pathlib 与传统 os.path 的对比
pathlib 相比传统 os.path 操作有明显优势:
| 操作 | os.path (传统方式) | pathlib (现代方式) |
|---|---|---|
| 路径拼接 | os.path.join('dir', 'subdir', 'file.txt') | path('dir') / 'subdir' / 'file.txt' |
| 获取文件名 | os.path.basename(path) | path.name |
| 获取父目录 | os.path.dirname(path) | path.parent |
| 检查存在 | os.path.exists(path) | path.exists() |
| 判断文件 | os.path.isfile(path) | path.is_file() |
| 判断目录 | os.path.isdir(path) | path.is_dir() |
pathlib 的面向对象方式使代码更简洁、更易读,而且自动处理操作系统差异,提高代码的可移植性。
6 总结
pathlib 库提供了一种现代化、面向对象的方法来处理文件系统路径,比传统的 os.path 模块更直观和强大。通过 path 对象,我们可以用更简洁的语法完成复杂的路径操作,同时自动处理不同操作系统的路径差异。
关键优势包括:
- 直观的路径拼接操作(使用
/运算符) - 丰富的路径解析方法(name、stem、suffix、parent 等)
- 便捷的文件系统操作(读写文件、目录遍历等)
- 更好的跨平台兼容性
- 更 pythonic 的 api 设计
对于新项目,建议直接使用 pathlib 处理所有文件路径操作,这将使代码更简洁、更易维护。
到此这篇关于基于python pathlib库的路径/文件/目录常用操作的文章就介绍到这了,更多相关python pathlib库路径/文件/目录操作内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论