文件操作
一、文件的读取和写入
1 | |
内置函数open会尝试打开文件并返回对应的文件对象,如果该文件无法打开,则会引发 OSError(具体的异常子类根据具体原因返回,例如文件不存在抛出 FileNotFoundError)。
主要参数详细说明:
file(必须):- 表示将要打开的文件的路径(绝对路径或者相对当前工作目录的路径)
mode(可选):- 指定打开文件的模式。详见下表“文件打开模式”。默认为
'r'。
模式 描述 注意事项 'r'只读 (默认) 如果文件不存在会报错 FileNotFoundError'w'写入 如果文件存在,会清空原内容;不存在则创建 'x'排他性创建 如果文件已存在,则报错(防止覆盖重要数据) 'a'追加 指针在文件末尾,不会清空原内容;不存在则创建 '+'打开用于更新(读取与写入) 指针在文件开头 'b'二进制模式 用于非文本文件 (如图片、音频),可与上述模式组合 (如 'rb','wb')'t'文本模式 默认模式,通常省略不写 'w+'和'w+b'模式将打开文件并清空内容。而'r+'和'r+b'模式将打开文件但不清空内容。- 指定打开文件的模式。详见下表“文件打开模式”。默认为
encoding(可选):- 仅文本模式使用。用于解码或编码文件的字符编码(如
'utf-8','gbk')。建议始终显式指定。
- 仅文本模式使用。用于解码或编码文件的字符编码(如
在使用open读写文件时,一定要使用close释放文件资源,或者使用with语句处理
不主动close可能发生的情况:
- 数据丢失(缓存未刷新): Python 的文件操作通常是有缓冲的(Buffering)。当你调用
f.write()时,数据可能还留在内存缓冲区中,并没有真正写入磁盘。如果你没有调用f.close()或f.flush(),且进程异常崩溃或强制终止,缓冲区中的数据可能会丢失。 - 文件锁定(Windows 尤为明显): 在 Windows 系统上,打开的文件通常会被锁定。如果你的程序是一个长期运行的服务(比如 Web 后台),而不主动关闭文件,其他程序将无法删除或修改该文件
示例:
1 | |
二、文件路径
我们在进行文件的读取和写入时,需要指定文件的路径,简单文件操作,直接传入一个固定的字符串即可;但是,如果我们想要对路径做更多更复杂的事情时,就需要借助其他模块,此处介绍两个内置模块os.path和pathlib。
1. os.path
此模块实现了一些有用的路径名称相关函数,下面介绍一些模块常用的API。
1.1 路径拼接与拆分
处理路径时,永远不要手动拼接字符串(如使用 + 或 /),而应使用专用函数。
手工拼接路径,可能会由于Windows和Linux对于路径的规则不一致而引起失败
| 函数 | 描述 |
|---|---|
os.path.join(path) |
路径拼接,根据当前操作系统自动插入正确的路径分隔符(\ 或 /) |
os.path.split(path) |
将路径拆分为“目录”和“文件名”两部分,返回一个元组 |
os.path.splitext(path) |
将路径文件名拆分为文件名和文件后缀,返回一个元组 |
示例:
1 | |
输出:
1 | |
1.2 路径属性获取
| 函数 | 描述 |
|---|---|
os.path.basename(path) |
返回文件名 |
os.path.dirname(path) |
返回目录路径 |
os.path.getsize(path) |
返回文件大小(字节) |
os.path.getmtime(path) |
返回 path 的最后修改时间。返回值是一个浮点数,为纪元秒数。如果文件不存在或不可访问,抛出 OSError异常 |
os.path.getatime(path) |
返回 path 的最后访问时间。返回值是一个浮点数,为纪元秒数。如果文件不存在或不可访问,抛出 OSError异常 |
os.path.getctime(path) |
返回 path 在系统中的 ctime,在有些系统(比如 Unix)上,它是元数据的最后修改时间,其他系统(比如 Windows)上,它是 path 的创建时间。返回值是一个数,为纪元秒数。如果文件不存在或不可访问,抛出 OSError异常 |
示例:
1 | |
输出:
1 | |
1.3 存在性与类型检查
| 函数 | 描述 |
|---|---|
os.path.exists(path) |
路径是否存在(文件或文件夹均可) |
os.path.isfile(path) |
是否为存在的文件 |
os.path.isdir(path) |
是否为存在的目录 |
os.path.isabs(path) |
是否为绝对路径 |
os.path.islink(path) |
是否为符号链接 |
示例:
1 | |
输出:
1 | |
1.4 路径规范化
| 函数 | 描述 |
|---|---|
os.path.abspath(path) |
将相对路径转换为绝对路径 |
os.path.normpath(path) |
清理路径中冗余的分隔符和相对引用(如 A/B/../C 变为 A/C) |
os.path.realpath(path) |
返回真实路径,并解析所有的符号链接(Symbolic Links) |
.:指当前目录..:当前目录的父目录
示例:
1 | |
1.5 跨平台
| 函数 | 描述 |
|---|---|
os.path.sep |
当前系统的路径分隔符(Windows 是 \,Linux 是 /) |
os.path.pathsep |
环境变量分隔符(Windows 是 ;,Linux 是 :) |
示例:
1 | |
2. pathlib
pathlib 是 Python 3.4+ 引入的标准库,旨在提供一种面向对象的方式来处理文件系统路径。它不仅涵盖了 os.path 的所有功能,还整合了 os 模块中关于文件操作(如创建目录、重命名等)的常用方法。
为什么选择 pathlib?
- 面向对象: 路径不再是字符串,而是具有丰富属性和方法的对象。
- 语法简洁: 使用
/运算符拼接路径,替代了冗长的os.path.join()。 - 链式操作: 可以通过
path.parent.parent这种方式轻松获取上级目录。 - 内置操作: 无需调用
open()即可完成简单的文件读写。
为适用不同的操作系统,pathlib将路径类按照 纯路径 和 具体路径 划分,其中纯路径仅提供计算操作;而具体路径是从纯路径继承而来,在提供计算操作的基础上提供了对 I/O 的操作。

2.1 纯路径 (Pure Paths)
纯路径类(PurePath)只提供计算操作,不涉及实际的 I/O 操作(不访问硬盘)。你可以在 Linux 系统上定义一个 Windows 格式的纯路径,这在处理路径字符串时非常有用。
PurePath: 通用基类。PureWindowsPath: 专门处理 Windows 风格路径(使用\)。PurePosixPath: 专门处理 Unix/Linux/macOS 风格路径(使用/)。
2.2 具体路径 (Concrete Paths)
具体路径类(Path)继承自纯路径,除了计算功能外,还可以执行系统调用(如创建文件、检查是否存在、读取内容)。
Path: 最常用的类。在实例化时,它会自动根据你的操作系统生成WindowsPath或PosixPath。WindowsPath: Windows 系统下的具体路径。PosixPath: Unix/Linux/macOS 系统下的具体路径。
注意:你不能在 Linux 上实例化
WindowsPath(会报错NotImplementedError: cannot instantiate 'WindowsPath' on your system),但你可以实例化PureWindowsPath
上边巴拉巴拉一大堆,但是在日常使用中,基本上直接使用 from pathlib import Path就足够满足对路径的操作和操作系统的访问。
2.3 主要API
2.3.1 路径拆解与属性 (与 os.path 对标)
| API (属性/方法) | 描述 | 对应 os.path |
|---|---|---|
p.name |
获取完整文件名(带后缀) | os.path.basename() |
p.stem |
获取文件名(不带后缀) | os.path.splitext()[0] |
p.suffix |
获取文件后缀(如 .py) |
os.path.splitext()[1] |
p.suffixes |
获取层级后缀列表(如 ['.tar', '.gz']) |
- |
p.parent |
获取父目录路径对象 | os.path.dirname() |
p.parents |
获取所有上级目录的序列(迭代器), | - |
p.anchor |
获取路径的根部分或驱动器号 | - |
p.parts |
将路径拆分为各级目录组成的元组 | - |
示例:
演示路径:/home/probie/workspace/python/base-python/test.py
1 | |
2.3.2 路径转换与拼接
| API | 描述 | 对应 os.path |
|---|---|---|
p1 / p2 |
使用 / 运算符拼接路径 |
os.path.join() |
p.joinpath(*parts) |
拼接多个路径部分 | os.path.join() |
p.absolute() |
获取绝对路径 | os.path.abspath() |
p.resolve() |
获取真实路径(解析符号链接和 ..) |
os.path.realpath() |
特别说明:/ 运算符 vs joinpath()
两者都用于拼接,但应用场景有所不同:
/运算符:- 语法更直观,类似于命令行中的路径表示,使用起来相对简洁且符合我们对路径的层级认识
- 要求至少左侧的对象是一个
Path对象 - 跨平台适用
- 示例:
Path("/usr") / "bin" / "python3"
joinpath()方法:- 更适合动态参数,因为它接受可变参数(
*args)。如果你有一个包含多个路径部分的列表或元组,使用它会更方便。 - 示例:
p.joinpath(*directories_list)
- 更适合动态参数,因为它接受可变参数(
示例:
1 | |
输出:
1 | |
2.3.3 状态检查与查询
| API | 描述 | 对应模块 |
|---|---|---|
p.exists() |
检查路径是否存在 | os.path.exists() |
p.is_file() |
是否为存在的文件 | os.path.isfile() |
p.is_dir() |
是否为存在的目录 | os.path.isdir() |
p.is_symlink() |
是否为符号链接(软链接) | os.path.islink() |
p.is_absolute() |
是否为绝对路径 | os.path.isabs() |
p.is_mount() |
是否为挂载点(Unix/Linux) | os.path.ismount() |
p.stat() |
获取文件状态(大小、修改时间等) | os.stat() |
p.owner() |
获取文件所有者用户名 | - |
p.group() |
获取文件所属组名 | - |
示例:
1 | |
输出:
1 | |
2.3.4 文件系统操作
| API | 描述 |
|---|---|
p.mkdir(mode=0x777,parents=False, exist_ok=False) |
创建目录,mode指定目录权限,parents找不到父级目录是否抛出异常,exist_ok在目录存在的情况下是否抛出异常 |
p.unlink() |
删除文件 |
p.rmdir() |
删除空目录 |
p.rename(target) |
重命名文件或目录 |
p.touch() |
创建空文件 |
示例:
1 | |
输出:
1 | |
2.3.5 路径修改
| API | 描述 |
|---|---|
p.with_name(name) |
替换文件名和后缀 |
p.with_stem(stem) |
替换文件名(保留原后缀) |
p.with_suffix(suffix) |
替换后缀名 |
p.with_segments(*parts) |
使用给定的片段创建一个新的路径对象 |
对Path对象的修改,即使路径不存在也不会发生错误,因为它不会和真实的操作系统交互
示例:
1 | |
输出:
1 | |
2.3.6 移动和复制
Python 3.14 版本添加
| API | 描述 |
|---|---|
Path.copy(target, follow_symlinks=True, preserve_metadata=False) |
将此文件或目录树拷贝到给定的 target,并返回一个指向 target 的新的 Path 实例。如果源文件是一个文件,则如果目标文件是一个已存在的文件,则目标文件将被替换。如果源是一个符号链接,并且 follow_symlinks 为true(默认值),则复制该符号链接的目标。否则,将在目的地重新创建符号链接。 如果 preserve_metadata 为false(默认值),则只保证复制目录结构和文件数据。将 preserve_metadata 设置为true,以确保在支持的地方复制文件和目录权限、标志、最后访问和修改时间以及扩展属性。此参数在Windows上复制文件时不起作用(会始终保留元数据)。 |
Path.move(target) |
将此文件或目录树移动到给定的 target,并返回一个指向 target 的新的 Path 实例。如果 target 不存在,它将被创建。 如果该路径和 target 都是现有文件,则覆盖目标。 如果两个路径都指向相同的文件或目录,或者 target 是非空目录,则引发 OSError |
在操作系统和文件系统支持的情况下,copy方法执行轻量级复制,数据块仅在被修改时才会被复制。这就是所谓的写时复制。
示例:
1 | |
输出:
1 | |
文件 test.txt变化顺序:
- 复制生成
test_copy.txt,此时同时存在test.txt和test_copy.txt - 移动
test.txt,此时生成test_move.txt,移动后文件变为test_copy.txt和test_move.txt
2.3.7 读取目录
测试目录结构:
1 | |
| API | 描述 |
|---|---|
path.iterdir() |
列出当前路径下的直接(不递归)目录和文件,不包括特殊条目 '.' 和 '..'。 |
path.glob(匹配模式,case_sensitive=None) |
case_sensitive大小写敏感,默认按照平台规则,Windows系统不区分,使用**递归匹配 |
path.rglob(匹配模式,case_sensitive=None) |
递归查找子目录,类似于 glob('**/xxx') |
path.walk() |
自上而下遍历目录树,返回一个三元组 (dirpath, dirnames, filenames);默认情况,遇到错误会忽略,可以通过on_error指定一个回调函数dirpath 是指向当前正被遍历到的目录的Path* dirnames存储dirpath路径下所有直接子目录的字符串名称filenames*存储dirpath路径下所有非目录的字符串名称 |
iterdir示例:
1 | |
glob示例:
1 | |
rglob示例:
1 | |
walk示例:
1 | |
3. 对比
pathlib模块中涵盖了 os 和 os.path 的大部分核心功能,但它们并非完全的替代关系。那么在实际项目开发中,我们应该如何选择呢?
- 新项目,对于路径的操作优先选择
pathlib,使用from pathlib import Path,涉及到底层os级别的,pathlib未提供的用os处理路径之外的系统交互 - 已有项目兼容,Path 对象转字符串:直接调用
str(path_obj),字符串转 Path 对象:直接Path(str_path)
pathlib对os和os.path模块功能的映射表: