一、pathlib vs os.path:两种路径处理方式的对比
Python提供了两套文件路径操作方案:传统的 os.path 模块(自Python 1.0起存在)和现代的 pathlib 模块(Python 3.4引入,3.6成为正式标准)。两套方案各有优劣,理解它们的设计哲学对于编写可维护的Python代码至关重要。传统 os.path 以字符串为中心,所有路径表示为普通字符串,通过大量函数进行操作;而 pathlib 采用面向对象设计,将路径封装为 Path 对象,提供了更直观的方法调用和运算符重载。
pathlib的设计理念是让路径操作更自然、更可读。使用 Path 对象而非纯字符串,可以利用面向对象的优势:方法串联、属性访问、运算符重载等。例如,使用 / 运算符进行路径拼接,比 os.path.join() 更加直观。此外,pathlib将文件系统的不同实体(纯路径、具体路径)进行了抽象,提供了 PurePath(纯路径,不涉及IO操作)和 Path(具体路径,可执行IO操作)两个层次。
os.path的优势在于兼容性和生态成熟度。大量遗留代码和第三方库仍采用字符串路径,与这些代码交互时 os.path 更为直接。此外,许多标准库函数(如 open()、os.listdir())原生接受字符串路径,使用 os.path 无需额外的类型转换。在Python版本方面,如果项目需要兼容Python 3.5及以下版本,os.path 是更安全的选择。
版本演进与选择建议:pathlib在Python 3.4作为试验性特性引入,3.6成为正式特性。从Python 3.6开始,pathlib被广泛认为是路径操作的首选方案。实际项目中,我们推荐以pathlib为主,在需要与字符串路径API交互时使用 str() 转换即可。对于新项目,应优先使用pathlib;对于维护遗留项目,可以在局部使用pathlib逐步重构。
以下代码对比两套方案的操作方式:
import os
import os.path
from pathlib import Path
# os.path方式:使用字符串和独立函数
base_dir = "/home/user/projects"
file_path = os.path.join(base_dir, "docs", "readme.txt")
print(os.path.dirname(file_path)) # /home/user/projects/docs
print(os.path.basename(file_path)) # readme.txt
# pathlib方式:使用Path对象和运算符
base = Path("/home/user/projects")
file = base / "docs" / "readme.txt"
print(file.parent) # /home/user/projects/docs
print(file.name) # readme.txt
# 判断文件是否存在
# os.path方式
if os.path.exists(file_path) and os.path.isfile(file_path):
print("文件存在")
# pathlib方式
if file.exists() and file.is_file():
print("文件存在")
# 获取文件扩展名
# os.path方式
name, ext = os.path.splitext("data.csv")
print(ext) # .csv
# pathlib方式
p = Path("data.csv")
print(p.suffix) # .csv
print(p.stem) # data
print(p.suffixes) # ['.csv'],支持多扩展名
二、Path对象详解
Path对象是pathlib的核心,它封装了文件系统路径的所有操作。创建Path对象有多种方式:可以直接传入字符串路径、使用类方法获取特殊路径、或者通过当前路径和家目录等起点进行构建。Path对象一旦创建,就可以通过一系列属性和方法对路径进行解析、拼接和转换。
创建Path对象的常见方式包括:直接传入字符串、使用 Path.cwd() 获取当前工作目录、使用 Path.home() 获取用户家目录。Path对象支持正斜杠 / 运算符重载,这是pathlib最受欢迎的设计之一——路径拼接变得像数学运算一样自然。拼接时,如果右侧以 / 开头,则被视为绝对路径拼接,会替换左侧部分。
路径解析属性是Path对象的一大亮点。通过 .parent 获取父目录(可连续使用,如 .parent.parent)、.name 获取最后一级名称、.suffix 获取扩展名、.stem 获取不含扩展名的文件名。这些属性比 os.path 中的对应函数更加直观简洁。此外,.parts 属性将路径拆分为元组,便于逐级处理。
绝对路径与相对路径转换也是常用操作。.resolve() 将相对路径解析为绝对路径并规范化(解析符号链接、消除 .. 和 .);.absolute() 简单加上当前工作目录转换为绝对路径但不做规范化;.relative_to() 计算相对于另一路径的相对路径。
from pathlib import Path
# 创建Path对象的多种方式
p1 = Path("docs/report.txt") # 相对路径
p2 = Path("/home/user/docs") # 绝对路径
p3 = Path.home() / "projects" # 家目录下
p4 = Path.cwd() # 当前工作目录
print(f"当前目录:{p4}")
print(f"家目录:{p3}")
# / 运算符路径拼接
config = Path("/etc") / "nginx" / "sites-available" / "default"
print(config) # /etc/nginx/sites-available/default
# 路径解析属性详解
p = Path("/home/user/docs/report.txt")
print(f"父目录:{p.parent}") # /home/user/docs
print(f"祖父目录:{p.parent.parent}") # /home/user
print(f"文件名:{p.name}") # report.txt
print(f"主文件名:{p.stem}") # report
print(f"扩展名:{p.suffix}") # .txt
print(f"路径组件:{p.parts}") # ('/', 'home', 'user', 'docs', 'report.txt')
# 绝对路径与相对路径转换
relative = Path("docs/../data/report.txt")
print(relative.resolve()) # 规范化后的绝对路径
# 计算相对路径
base = Path("/home/user")
target = Path("/home/user/docs/file.txt")
print(target.relative_to(base)) # docs/file.txt
# 处理带空格的路径
path_with_space = Path("/home/user/My Documents/file.txt")
print(path_with_space.as_posix()) # Unix风格斜杠
# Windows路径转换
import sys
if sys.platform == "win32":
win_path = Path("C:\\Users\\admin\\file.txt")
print(win_path.as_posix()) # C:/Users/admin/file.txt
print(win_path.drive) # C:
三、文件与目录操作
pathlib将常见的文件与目录操作封装为Path对象的方法,让代码更加简洁一致。相比于 os 和 shutil 模块中分散的函数,pathlib提供了一站式的操作体验:创建目录、读写文件、重命名、删除等操作都可以通过Path对象的方法直接完成。
创建与删除目录:.mkdir() 方法创建目录,通过 exist_ok=True 避免目录已存在时抛出异常,parents=True 同时创建所有不存在的父目录(类似 mkdir -p)。.rmdir() 删除空目录,.rmdir() 要求目录为空,否则抛出异常。
文件读写操作是pathlib相比 os.path 的一大优势。.read_text() 和 .write_text() 方法可以直接读取和写入文本文件,无需手动管理文件打开和关闭。同样,.read_bytes() 和 .write_bytes() 用于二进制文件的读写。这些方法内部自动处理资源释放,代码更加简洁安全。
文件重命名、移动与删除:.rename() 和 .replace() 重命名或移动文件(replace 在目标存在时会强制覆盖),.unlink() 删除文件。值得注意的是,这些操作对文件和目录都适用,但 .unlink() 用于文件,.rmdir() 仅用于空目录。
from pathlib import Path
# 创建目录(类似 mkdir -p)
data_dir = Path("project/data/2026")
data_dir.mkdir(parents=True, exist_ok=True)
print("目录创建成功")
# 创建多个子目录
for month in ["01", "02", "03"]:
(data_dir / month).mkdir(parents=True, exist_ok=True)
# 文件读写操作
file_path = Path("example.txt")
# 写入文本(自动编码处理)
file_path.write_text("Hello, Python!\n这是pathlib写入的内容。", encoding="utf-8")
# 读取文本
content = file_path.read_text(encoding="utf-8")
print(content)
# 二进制文件操作
binary_path = Path("image.png")
if binary_path.exists():
data = binary_path.read_bytes()
print(f"文件大小:{len(data)} 字节")
# 追加写入(使用标准open)
with file_path.open("a", encoding="utf-8") as f:
f.write("这是追加的内容。\n")
# 文件重命名、移动和删除
src = Path("old_report.txt")
dst = Path("archive/2026_report.txt")
# 先确保目标目录存在
dst.parent.mkdir(parents=True, exist_ok=True)
# 重命名/移动文件
src.rename(dst) # 将旧文件移动到新位置并改名
# 强制覆盖移动(推荐使用replace)
# src.replace(dst) # 目标存在时覆盖
# 删除文件
if dst.exists():
dst.unlink() # 删除文件
print("文件已删除")
# 删除目录(只能删除空目录)
empty_dir = Path("temp_dir")
if empty_dir.exists():
empty_dir.rmdir() # 目录必须为空
四、路径信息与属性
获取文件或目录的详细信息是文件和系统操作中的常见需求。pathlib通过 .stat() 方法(对应 os.stat())提供了全面的文件元信息查询能力,包括文件大小、修改时间、访问权限等。同时,pathlib还提供了多个便捷的属性方法用于文件类型判断和权限检查。
文件统计信息:.stat() 返回一个 os.stat_result 对象,包含文件的所有元数据:.st_size 文件大小(字节)、.st_mtime 最后修改时间(时间戳)、.st_atime 最后访问时间、.st_ctime 创建时间(Windows)或元数据修改时间(Unix)。获取时间戳后可以使用 datetime 模块转换为可读的日期格式。
文件类型判断:pathlib提供了一系列方法来判断路径的类型:.is_file() 是否为普通文件、.is_dir() 是否为目录、.is_symlink() 是否为符号链接、.is_socket() 是否为套接字文件(Unix)、.is_fifo() 是否为命名管道(Unix)。这些方法内部调用 os.stat() 但提供了更友好的接口。
权限检查:.stat().st_mode 包含文件的权限信息。pathlib提供了 .owner() 获取文件所有者用户名、.group() 获取文件所属组名。结合权限位掩码可以检查文件的读写执行权限。在自动化办公场景中,这些信息常用于日志文件轮转、备份策略中的文件筛选。
from pathlib import Path
import datetime
import stat
# 获取文件详细信息
file_path = Path("data.txt")
if file_path.exists():
info = file_path.stat()
print(f"文件大小:{info.st_size} 字节")
print(f"占用空间:{info.st_size / 1024:.2f} KB")
# 时间戳转可读格式
mtime = datetime.datetime.fromtimestamp(info.st_mtime)
print(f"最后修改时间:{mtime.strftime('%Y-%m-%d %H:%M:%S')}")
atime = datetime.datetime.fromtimestamp(info.st_atime)
print(f"最后访问时间:{atime.strftime('%Y-%m-%d %H:%M:%S')}")
# 文件类型和权限检查
target = Path("target_dir")
# 文件类型判断
print(f"是文件吗?{target.is_file()}")
print(f"是目录吗?{target.is_dir()}")
# 符号链接处理
link = Path("my_link")
if link.is_symlink():
print(f"链接目标:{link.readlink()}")
# 所有者信息
if target.exists():
print(f"所有者:{target.owner()}")
print(f"所属组:{target.group()}")
# 权限检查
mode = target.stat().st_mode
print(f"是否可读:{bool(mode & stat.S_IRUSR)}")
print(f"是否可写:{bool(mode & stat.S_IWUSR)}")
print(f"是否可执行:{bool(mode & stat.S_IXUSR)}")
# 批量获取目录中所有文件的信息
from pathlib import Path
directory = Path("downloads")
if directory.exists() and directory.is_dir():
print("文件名\t\t大小\t\t修改时间")
print("-" * 50)
for f in directory.iterdir():
if f.is_file():
info = f.stat()
mtime = datetime.datetime.fromtimestamp(info.st_mtime)
print(f"{f.name}\t\t{info.st_size}\t\t{mtime.strftime('%m-%d %H:%M')}")
五、目录遍历
目录遍历是文件系统操作的核心需求之一。pathlib提供了多种遍历方式,从简单的单层遍历到深度递归的模式匹配,覆盖了从简单到复杂的所有场景。.iterdir() 适用于简单遍历,.glob() 适用于模式匹配,.rglob() 适用于递归搜索。
iterdir遍历:.iterdir() 返回目录下所有项目的生成器(不会递归子目录)。遍历结果包含文件和子目录,返回的是Path对象列表。可以在遍历时使用 .is_file() 或 .is_dir() 进行过滤。由于返回的是生成器,对于大目录来说内存友好。
glob模式匹配:.glob(pattern) 使用Unix shell风格的通配符匹配文件,支持 *(匹配任意多个字符,不包括 /)、?(匹配单个字符)、[abc](字符集匹配)。.rglob(pattern) 是递归版本的glob,会搜索所有子目录。两者都返回生成器,按需产生匹配结果。
文件过滤与目录树:结合各种条件和列表推导式,可以轻松实现复杂的文件过滤逻辑。例如筛选特定时间范围内的文件、按大小过滤文件、组合多个过滤条件等。利用Python的生成器表达式和列表推导式,可以编写出非常简洁而强大的目录遍历代码。
from pathlib import Path
# iterdir 基本遍历
data_dir = Path("data")
if data_dir.exists():
print("=== data目录下的所有项目 ===")
for item in data_dir.iterdir():
type_str = "目录" if item.is_dir() else "文件"
print(f"[{type_str}] {item.name}")
# 只列出文件
files = [f for f in data_dir.iterdir() if f.is_file()]
print(f"共 {len(files)} 个文件")
# glob 模式匹配
print("=== 所有Python文件 ===")
for py_file in Path(".").glob("*.py"):
print(py_file)
# rglob 递归搜索
print("=== 搜索所有CSV文件(递归)===")
for csv_file in Path("project").rglob("*.csv"):
print(csv_file)
# 组合多个模式
print("=== 图片文件 ===")
image_patterns = ["*.jpg", "*.png", "*.gif", "*.webp"]
for pattern in image_patterns:
for img in Path("assets").rglob(pattern):
print(img)
# 文件过滤:按日期和大小
import datetime
logs_dir = Path("logs")
cutoff = datetime.datetime.now() - datetime.timedelta(days=7)
# 找出最近7天内修改过的日志文件
recent_logs = [
f for f in logs_dir.rglob("*.log")
if f.is_file()
and datetime.datetime.fromtimestamp(f.stat().st_mtime) > cutoff
]
print(f"最近7天修改的日志文件:{len(recent_logs)} 个")
# 找出大于100MB的文件
large_files = [
f for f in Path("data").rglob("*")
if f.is_file() and f.stat().st_size > 100 * 1024 * 1024
]
print(f"大文件(>100MB):{len(large_files)} 个")
六、os模块互补
尽管pathlib极大地简化了路径操作,但 os 模块在某些系统级操作方面仍然不可或缺。os 模块提供了与操作系统交互的核心功能,包括进程管理、环境变量、系统信息等,这些是pathlib没有覆盖的领域。在实际开发中,pathlib与os模块经常配合使用,各取所长。
工作目录与环境变量:os.getcwd() 获取当前工作目录,os.chdir() 切换工作目录。这两个函数在自动化脚本中经常用到,特别是在需要临时切换工作目录处理文件时。os.environ 提供了环境变量的字典接口,支持读取和设置系统环境变量,这对于配置管理非常重要。需要注意的是,修改 os.environ 只影响当前进程及其子进程。
跨平台系统信息:os.name 返回操作系统名称('posix'、'nt'、'java'),os.system() 执行系统命令。在自动化办公中,os.system() 可用于执行简单的shell命令,但更推荐使用 subprocess 模块进行复杂命令执行。此外,os.sep 和 os.linesep 提供了平台相关的路径分隔符和换行符。
os.walk目录遍历:os.walk() 是一个强大的目录树遍历生成器,返回三元组 (dirpath, dirnames, filenames)。相比pathlib的 rglob,os.walk() 提供了更细粒度的控制,如通过修改 dirnames 列表可以动态控制进入哪些子目录(剪枝)。这在需要排除特定目录(如 .git、node_modules)时特别有用。
import os
# 工作目录操作
original_dir = os.getcwd()
print(f"当前工作目录:{original_dir}")
# 切换目录并执行操作
os.chdir("/tmp")
print(f"切换后目录:{os.getcwd()}")
# 切回原目录
os.chdir(original_dir)
# 环境变量操作
print(f"PATH环境变量:{os.environ.get('PATH', '未设置')[:50]}...")
# 设置临时环境变量
os.environ["MY_APP_MODE"] = "development"
print(f"MY_APP_MODE:{os.environ.get('MY_APP_MODE')}")
# os.walk 目录遍历(带排除)
import os
exclude_dirs = {".git", "node_modules", "__pycache__"}
print("=== 项目文件结构(排除.git等目录)===")
for root, dirs, files in os.walk("my_project"):
# 从dirnames中排除特定目录(实现剪枝)
dirs[:] = [d for d in dirs if d not in exclude_dirs]
level = root.replace("my_project", "").count(os.sep)
indent = " " * level
print(f"{indent}{os.path.basename(root)}/")
sub_indent = " " * (level + 1)
for file in files:
print(f"{sub_indent}{file}")
# os.system 执行系统命令(配合pathlib使用)
import os
from pathlib import Path
# 使用pathlib创建目录,用os执行命令
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
# 执行系统命令(仅限简单命令,复杂命令用subprocess)
exit_code = os.system("echo '系统命令执行完毕'")
print(f"命令退出码:{exit_code}")
# 获取系统信息
print(f"操作系统:{os.name}")
print(f"路径分隔符:'{os.sep}'")
print(f"换行符:{repr(os.linesep)}")
七、跨平台处理
跨平台兼容性是文件路径操作中最重要的考量之一。Windows使用反斜杠 \ 作为路径分隔符,而Linux和macOS使用正斜杠 /。此外,Windows还有驱动器号(如 C:)和UNC路径等特殊概念。pathlib通过抽象层优雅地处理了这些差异,让开发者编写一次代码即可在多个平台上运行。
路径分隔符差异:pathlib内部始终使用正斜杠 / 作为路径表示,在Windows上会自动转换为反斜杠。使用 / 运算符拼接路径可以确保跨平台正确性。如果需要在代码中硬编码路径,应始终使用正斜杠 /,pathlib会处理平台适配。
PurePath可移植路径:PurePath 是 Path 的基类,只提供路径计算的纯操作(如拼接、解析),不执行任何文件系统IO。PurePosixPath 和 PureWindowsPath 分别对应于Unix风格和Windows风格的纯路径操作。在需要处理与当前平台不同的路径格式时(例如在Windows上解析Linux路径),PurePath特别有用。
跨平台注意事项:除了路径分隔符,还需要注意文件系统大小写敏感性(Linux区分大小写,Windows不区分)、特殊字符处理(冒号在Windows文件名中不允许)、路径长度限制(Windows MAX_PATH限制为260字符)、以及权限模型差异。pathlib的 Path 对象在Windows上会自动处理长路径前缀(\\?\)。
from pathlib import Path, PurePosixPath, PureWindowsPath
import sys
# 跨平台路径拼接(始终使用正斜杠)
config = Path("config/settings.json")
print(config) # 当前平台格式:Linux下为config/settings.json
# 在任意平台上处理不同风格的路径
unix_path = PurePosixPath("/home/user/docs/file.txt")
win_path = PureWindowsPath("C:\\Users\\admin\\docs\\file.txt")
print(f"Unix路径各部分:{unix_path.parts}")
print(f"Windows驱动器:{win_path.drive}")
print(f"Windows路径各部分:{win_path.parts}")
# 跨平台兼容的路径处理
def get_data_path(filename: str) -> Path:
"""返回数据文件路径,兼容所有平台"""
base = Path(__file__).parent.resolve()
return base / "data" / filename
# 使用
data_file = get_data_path("report.csv")
print(f"数据文件路径:{data_file}")
# 路径比较(Windows上不区分大小写)
p1 = Path("README.TXT")
p2 = Path("readme.txt")
if sys.platform == "win32":
print(p1 == p2) # True(Windows不区分大小写)
else:
print(p1 == p2) # False(Linux区分大小写)
# 路径格式转换与网络路径
from pathlib import PureWindowsPath
# Windows网络路径(UNC)
unc = PureWindowsPath("\\\\server\\share\\folder\\file.txt")
print(f"UNC服务器:{unc.parts[0]}")
# Windows路径转Unix风格
win_path = PureWindowsPath("C:\\Users\\admin")
print(win_path.as_posix()) # C:/Users/admin
# 判断路径是否为绝对路径
print(PurePosixPath("/etc").is_absolute()) # True
print(PureWindowsPath("C:/Windows").is_absolute()) # True
print(PureWindowsPath("rel/folder").is_absolute()) # False
八、最佳实践
在文件路径与系统操作方面,遵循一些最佳实践可以避免常见的陷阱,编写出更健壮、更可维护的代码。以下是在实际项目中积累的路径操作经验和注意事项。
路径异常处理:文件系统操作天然容易出错——文件不存在、权限不足、磁盘已满、路径非法等。使用pathlib时,应当对关键操作进行异常捕获。常见的异常包括 FileNotFoundError(文件不存在)、PermissionError(权限不足)、FileExistsError(文件已存在)、NotADirectoryError(不是目录)和 OSError(其他系统错误)。合理的异常处理策略是:预期可能失败的操作用try/except包裹,非关键错误给出警告而非崩溃。
资源清理与上下文管理:使用pathlib的文件读写方法(如 read_text())自动管理资源释放。对于更复杂的文件操作,使用 with 语句确保资源正确清理。当创建临时文件或目录时,务必在完成后清理,可以使用 tempfile 模块中的 TemporaryDirectory 上下文管理器自动清理。
编码问题:在不同操作系统上,文件名的编码方式可能不同(Linux使用UTF-8,Windows使用系统代码页)。使用pathlib的 read_text() 和 write_text() 时始终指定 encoding="utf-8"。在处理用户提供的文件名时,注意处理特殊字符(如 /、\0、: 等)。路径缓存:在需要多次访问同一路径信息时(如多次调用 .stat()),将结果存储在变量中避免重复系统调用。
from pathlib import Path
# 稳健的路径操作模式
def safe_read_file(file_path: Path) -> str:
"""安全读取文件内容,处理各种异常"""
try:
if not file_path.exists():
return ""
if not file_path.is_file():
raise ValueError(f"{file_path} 不是文件")
return file_path.read_text(encoding="utf-8")
except PermissionError:
print(f"警告:无权限读取 {file_path}")
return ""
except OSError as e:
print(f"读取文件失败:{e}")
return ""
# 使用示例
content = safe_read_file(Path("config.json"))
# 临时目录和资源清理
import tempfile
from pathlib import Path
# 使用上下文管理器自动清理临时目录
with tempfile.TemporaryDirectory(prefix="my_app_") as tmp_dir:
tmp_path = Path(tmp_dir)
# 在临时目录中工作
work_file = tmp_path / "temp_data.txt"
work_file.write_text("临时数据", encoding="utf-8")
print(f"临时文件已创建:{work_file}")
print(f"内容:{work_file.read_text(encoding='utf-8')}")
# 退出with块后,临时目录自动清理
print("临时目录已清理")
# 路径缓存示例
def get_dir_info(directory: Path) -> dict:
"""获取目录信息,使用变量缓存stat结果"""
if not directory.exists():
return {}
info = directory.stat() # 只调用一次stat
return {
"size": info.st_size,
"modified": info.st_mtime,
"created": info.st_ctime,
}
# 编码处理和安全文件名
import re
def safe_filename(name: str) -> str:
"""移除去掉不适合用作文件名的字符"""
# 替换Windows和Linux中不合法的文件名字符
return re.sub(r'[<>:"/\\|?*]', '_', name)
# 使用Path处理各种编码场景
title = "数据报表: 2026/05/05 (季度)"
safe_name = safe_filename(title)
file_path = Path("reports") / f"{safe_name}.xlsx"
file_path.parent.mkdir(exist_ok=True)
print(f"安全文件名:{file_path}")
九、实战案例
将前面学到的知识综合运用,通过三个完整的实战案例展示pathlib和os模块在实际项目中的应用。每个案例都涵盖了路径操作、目录遍历、文件处理等核心技能,是日常自动化工作中最常见的使用场景。
案例一:项目文件结构分析。编写一个工具函数,扫描项目目录并生成文件结构的统计报告。该工具需要递归遍历目录,统计各类文件的数量、总大小、最近修改时间等,并以格式化的方式输出结果。在遍历过程中,需要排除 .git、node_modules、__pycache__ 等非项目文件目录。这个工具在项目管理和代码审查中非常实用。
案例二:日志文件归档。服务器日志文件通常按日增长,需要定期归档清理。实现一个日志归档脚本:查找指定目录下超过30天的旧日志文件,将它们移动到归档目录并压缩(使用gzip),然后删除原文件。脚本需要记录归档操作日志,并在遇到权限问题或文件锁定等异常时优雅处理。
案例三:临时文件夹管理。自动化脚本常常需要创建临时文件夹存放中间结果。设计一个智能临时文件夹管理器,使用 tempfile 创建临时目录,将中间文件写入其中,在任务完成时确保所有临时文件被清理。同时支持异常情况下的清理(使用try/finally或上下文管理器)。
# 案例一:项目文件结构分析
from pathlib import Path
import datetime
def analyze_project(project_dir: str):
"""分析项目文件结构并生成统计报告"""
base = Path(project_dir).resolve()
exclude = {".git", "node_modules", "__pycache__", ".idea", "venv", ".DS_Store"}
stats = {"total_files": 0, "total_dirs": 0, "total_size": 0, "by_ext": {}}
newest = None
for item in base.rglob("*"):
if any(p in exclude for p in item.parts):
continue
if item.is_file():
stats["total_files"] += 1
size = item.stat().st_size
stats["total_size"] += size
ext = item.suffix or "(无扩展名)"
stats["by_ext"][ext] = stats["by_ext"].get(ext, 0) + 1
mtime = item.stat().st_mtime
if newest is None or mtime > newest[1]:
newest = (item, mtime)
elif item.is_dir():
stats["total_dirs"] += 1
# 输出报告
print("=" * 50)
print(f"项目分析报告:{base}")
print("=" * 50)
print(f"目录数:{stats['total_dirs']}")
print(f"文件数:{stats['total_files']}")
print(f"总大小:{stats['total_size'] / 1024:.2f} KB")
print("\n文件类型分布:")
for ext, count in sorted(stats["by_ext"].items()):
print(f" {ext:>12} : {count} 个")
if newest:
mtime_str = datetime.datetime.fromtimestamp(newest[1]).strftime("%Y-%m-%d %H:%M")
print(f"\n最近修改:{newest[0].name} ({mtime_str})")
# 使用示例
analyze_project(".")
# 案例二:日志文件归档
from pathlib import Path
import datetime
import gzip
import shutil
def archive_old_logs(log_dir: str, days_old: int=30, archive_dir: str=None):
"""归档超过指定天数的日志文件"""
log_path = Path(log_dir)
if not log_path.exists():
print(f"日志目录不存在:{log_dir}")
return
archive_path = Path(archive_dir or log_path / "archive")
archive_path.mkdir(parents=True, exist_ok=True)
cutoff = datetime.datetime.now() - datetime.timedelta(days=days_old)
archived_count = 0
for log_file in log_path.glob("*.log"):
mtime = datetime.datetime.fromtimestamp(log_file.stat().st_mtime)
if mtime < cutoff:
try:
# 压缩并归档
archive_name = archive_path / f"{log_file.stem}_{mtime.strftime('%Y%m%d')}.log.gz"
with log_file.open("rb") as f_in:
with gzip.open(archive_name, "wb") as f_out:
shutil.copyfileobj(f_in, f_out)
# 删除原文件
log_file.unlink()
archived_count += 1
print(f"已归档:{log_file.name} -> {archive_name.name}")
except (PermissionError, OSError) as e:
print(f"归档失败 {log_file.name}:{e}")
print(f"\n归档完成,共处理 {archived_count} 个文件")
# 使用示例
# archive_old_logs("/var/log/myapp", days_old=30)
# 案例三:临时文件夹管理器
from pathlib import Path
import tempfile
import shutil
class TempWorkspace:
"""安全的临时工作目录管理器"""
def __init__(self, prefix: str="workspace_"):
self.prefix = prefix
self.path = None
def __enter__(self):
self.path = Path(tempfile.mkdtemp(prefix=self.prefix))
print(f"创建临时目录:{self.path}")
return self.path
def __exit__(self, exc_type, exc_val, exc_tb):
if self.path and self.path.exists():
shutil.rmtree(self.path, ignore_errors=True)
print(f"清理临时目录:{self.path}")
# 使用临时工作区处理文件
def process_files(input_files):
"""在临时工作区中处理文件"""
with TempWorkspace(prefix="file_process_") as work_dir:
print("正在处理文件...")
# 复制输入文件到工作区
for f in input_files:
src = Path(f)
if src.exists():
shutil.copy2(src, work_dir / src.name)
# 在工作区中进行处理
for item in work_dir.iterdir():
if item.is_file():
content = item.read_text(encoding="utf-8")
item.write_text(content.upper(), encoding="utf-8")
print("处理完成!结果保存在临时目录中")
return work_dir
# 退出with块后,临时目录自动被清理
# 不需要手动删除