玩转Python文件系统工具库(一)
Python的一大用途就是对文件系统进行各种操作,如文件批量改名、替换内容、遍历目录等等,掌握这些文件系统基本操作的常常是入门Python时的最大乐趣。尽管如此,很多教程往往将文件系统的操作作为示例中的甜点,而没有系统地去介绍这部分内容。所以本节开始我们讨论Python中的文件系统操作。
1、Python内置的文件系统操作工具
Python 标准库提供了多个内置模块来进行文件和目录操作。常用的主要有:
| 模块 | 作用简介 |
|---|---|
| os | 老式的文件系统接口,处理权限、低层访问等 |
| pathlib | 现代的面向对象路径操作方式,跨平台、安全 |
| shutil | 提供复制、移动、压缩等高级文件和目录操作 |
| tempfile | 创建临时文件和目录 |
| zipfile | 操作 .zip 压缩文件 |
| fnmatch | Unix 风格的文件名匹配(类似 shell 的 *.txt) |
这里值得注意的是:在Python3.4之前,关于文件路径的操作功能主要被放在了os.path 模块中,而Python3.4推出了全新的pathlib库,它采用了面向对象的设计理念,将路径视为一个对象,并为此提供了丰富的属性和方法来进行各种操作。这种方式不仅代码更加简洁明了,也减少了错误的发生。
pathlib的类结构大体如下:
| 类名 | 用途 |
|---|---|
| Path | 核心类,表示一个文件或目录路径(自动适配 OS) |
| PurePath | 不涉及文件系统的路径类(只做字符串处理) |
| PurePosixPath | POSIX 专用的纯路径 |
| PureWindowsPath | Windows 专用的纯路径 |
| PosixPath | POSIX 实际路径类(Linux/macOS 使用) |
| WindowsPath | Windows 实际路径类(Windows 使用) |
2、路径与文件名操作
在开始讨论文件系统的操作前,我们必须对文件/目录的路径有一个基本的认识,我们知道在计算机中,文件 / 目录的路径用于指示文件或目录在文件系统中的具体位置,它就像是一份 “地图”,帮助操作系统和用户快速找到所需的文件或目录。
路径的结构
在文件系统中,一个完整的路径通常由以下几个部分组成:
路径结构示意(类 Unix 和 Windows 类似):
/home/user/docs/report.txt
│ │ │ └── 文件名(name)
│ │ └── 上级目录(parent)
│ └── 用户目录
└── 根目录(根路径)
对此pathlib将为此而生成一个Path对象,并进行如下解析:
| 组件 | pathlib 属性 | 示例值 |
|---|---|---|
| 根路径 | .anchor | ‘/’(Linux), ‘C:\’(Windows) |
| 父路径 | .parent | ‘home/user/docs’ |
| 文件名 | .name | ‘report.txt’ |
| 主干名 | .stem | ‘report’ |
| 扩展名 | .suffix | ‘.txt’ |
| 所有后缀 | .suffixes | [‘.tar’, ‘.gz’](多扩展名) |
我们可以通过以下代码获得需要的部分:
from pathlib import Path
p = Path("some/folder/file.txt")
print(p.name) # 文件名:file.txt
print(p.stem) # 主干名:file
print(p.suffix) # 扩展名:.txt
print(p.parent) # 父目录:some/folder
print(p.resolve()) # 返回绝对路径
路径的组合
通过pathlib我们不仅可以解析路径字符串,也能根据需要进行路径组合:
base = Path("/home/user")
full = base / "documents" / "file.txt"
print(full) # 输出:/home/user/documents/file.txt
路径的判断
我们在开始使用路径时,往往需要进行一些必要的判断,如判断路径是否存在、是否是文件或目录:
print(p.exists()) # True / False
print(p.is_file()) # 是否为文件
print(p.is_dir()) # 是否为目录
3、目录的操作
创建、删除目录:
Path("data/logs").mkdir(parents=True, exist_ok=True) # 递归创建目录
Path("data/logs").rmdir() # 删除空目录
递归删除目录
shutil模块的rmtree()函可以递归地删除目录及其子目录中的所有文件和目录,因此要谨慎使用。
import shutil
shutil.rmtree("my_dir")
遍历当前目录下所有内容:
for item in Path(".").iterdir():
print(item)
递归遍历当前目录及子目录的所有内容:
for path in Path(".").rglob("*"):
print(path)
当然很多小伙伴可能更熟悉这种方式:
import os
for root, dirs, files in os.walk("my_dir"):
for file in files:
print(os.path.join(root, file))
递归查找 .txt 文件:
for file in Path(".").rglob("*.txt"):
print(file)
glob 函数支持多种通配符来匹配文件路径模式,以下各种通配符的具体说明:
| 通配符 | 含义说明 | 示例 |
|---|---|---|
| * | 匹配任意数量的字符(包括空字符),不跨目录 | *.txt 匹配所有 .txt 文件 |
| ? | 匹配任意单个字符 | file?.py 匹配 file1.py, fileA.py |
| [abc] | 匹配 a、b 或 c 中的任意一个字符 | file[12].txt 匹配 file1.txt, file2.txt |
| [a-z] | 匹配指定范围内的字符 | [a-z]*.md 匹配以小写字母开头的 .md 文件 |
| [!abc] | 匹配不是 a、b 或 c 的字符(否定匹配) | file[!3].txt 匹配除了 file3.txt 之外的 |
| ** | 匹配任意层级目录(用于递归) | *.py 匹配当前目录及子目录中所有 .py 文件(需使用 rglob() 或 glob(“/”)) |
4、文件的操作
文件的操作主要借助于shutil库。
import shutil
src = Path("a.txt")
dst = Path("backup/b.txt")
src.rename(dst) # 重命名或移动
dst.unlink() # 删除文件
shutil.copy(src, dst) # 复制文件
shutil.copytree(Path("dir1"), Path("dir2")) # 复制目录树
注意,使用unlink()删除文件时,它的意思是直接永久删除,如果只是需要放入操作系统的回收站或废纸篓可以使用第三方库send2trash。
from send2trash import send2trash
from pathlib import Path
file = Path("important.txt")
if file.exists():
send2trash(file)
print("文件已移至回收站")
else:
print("文件不存在")
5、文件的读写
文件信息与元数据
p = Path("example.txt")
info = p.stat()
print("大小:", info.st_size, "字节")
print("创建时间:", info.st_ctime)
print("修改时间:", info.st_mtime)
文件的读写
p = Path("notes.txt")
p.write_text("Hello, world!", encoding="utf-8") # 写入文本
text = p.read_text(encoding="utf-8") # 读取文本
p.write_bytes(b"binary data") # 写入二进制
data = p.read_bytes() # 读取二进制
当然很多小伙伴可能更熟悉这种方式:
import os
file_path = os.path.join("data", "example.txt")
# 确保目录存在
os.makedirs(os.path.dirname(file_path), exist_ok=True)
# 全部读取文本文件
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
print(content)
# 按行读取文本文件
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
print("行内容:", line.strip())
# 写入文本文件
with open(file_path, "w", encoding="utf-8") as f:
f.write("这是写入的内容\n第二行内容")
open() 函数包含了一个文件打开模式参数,其模式有以下几种:
| 模式 | 含义 |
|---|---|
| r | 以只读方式打开文本文件 |
| w | 写入文本文件(覆盖原内容) |
| a | 追加到文本文件末尾 |
| rb | 以二进制方式读取 |
| wb | 以二进制方式写入(覆盖原内容) |
| x | 写入新文件(若文件已存在则报错) |
6、文件/目录权限控制
权限代码
Python 中通常使用三位UNIX 风格的八进制整数(如 0o755),表示用户、用户组和其他人对文件/目录的操作权限。
rwxr-xr-- = 0o754
│ │ │
│ │ └── 其他用户权限:只读 (4)
│ └──── 组权限:可读+可执行 (5)
└────── 所有者权限:可读+写+执行 (7)
下面是一些常见权限模式:
| 模式 | 八进制 | 说明 |
|---|---|---|
| rw-r–r– | 0o644 | 普通文件,用户读写,组和其他只读 |
| rwxr-xr-x | 0o755 | 可执行脚本/程序,用户可读写执行 |
| rw------- | 0o600 | 私密文件,只有所有者读写权限 |
| rwx------ | 0o700 | 私密目录或脚本,仅用户可执行 |
权限设置
我们可以使用 Path.chmod() 进行权限设置,使用 os.access() 进行权限检查。
import os
p = Path("example.txt")
p.chmod(0o600) # 设置为用户读写权限
print(os.access(p, os.R_OK)) # 判断是否可读
7、临时文件和目录
在程序中,有时候我们需要短暂使用文件系统存储,比如缓存、临时数据处理等,我们可以通过tempfile创建临时文件和临时目录。
import tempfile
# 临时二进制文件
with tempfile.TemporaryFile() as tmp:
tmp.write(b"Hello, temp!")
tmp.seek(0)
print(tmp.read()) # 输出:b'Hello, temp!'
# 临时文本文件
with tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8', delete=True) as tmp:
tmp.write("临时内容")
tmp.seek(0)
print(tmp.read())
print(tmp.name) # 查看临时文件路径
其中:
NamedTemporaryFile 会产生一个文件名路径,适合传递给其他程序使用。
delete=True 表示退出with时自动删除文件。
8、压缩与解压
压缩单一文件
import zipfile
with zipfile.ZipFile("archive.zip", "w") as z:
z.write("example.txt")
压缩整个目录
shutil.make_archive()可以快速将整个目录打包成 .zip 或 .tar.gz 文件。
import shutil
# 参数说明:
# - base_name: 输出文件路径(不带扩展名)
# - format: 压缩格式,如 'zip', 'tar', 'gztar', 'bztar', 'xztar'
# - root_dir: 要打包的目录
shutil.make_archive("output/archive", "zip", root_dir="my_folder")
zipfile则可以灵活地控制哪些文件被压缩,或如何在压缩包中命名路径。
import zipfile
from pathlib import Path
def zip_dir(dir_path, zip_path):
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
for file in Path(dir_path).rglob("*"):
if file.is_file():
# 将文件添加到 zip 包,并保留相对路径结构
z.write(file, arcname=file.relative_to(dir_path))
zip_dir("my_folder", "output/custom_archive.zip")
解压文件
import zipfile
with zipfile.ZipFile("archive.zip", "r") as z:
z.extractall("unzipped/")
1325

被折叠的 条评论
为什么被折叠?



