Python怎么获取文件大小和修改时间?os.stat与pathlib属性读取方法
在Python中获取文件大小与最后修改时间,主要通过标准库中的os.stat与pathlib模块实现。本文详细介绍st_size与st_mtime属性的读取方式、时间戳格式化转换、字节大小人性化展示,以及两种实现方式的区别与适用场景。
os.stat() 或 pathlib.Path.stat()。两者底层均通过单次操作系统级调用检索文件的元数据,分别通过 st_size 属性获取以字节为单位的文件大小,以及通过 st_mtime 属性获取 Unix 时间戳格式的修改时间。本文将分步骤介绍如何准确读取这两个核心属性、如何将原始数据转换为人类易读的格式,并对比不同使用场景下的技术选型。
一、底层原理:操作系统元数据与 stat_result 结构体
当程序向操作系统查询某个文件的信息时,操作系统并不会去扫描文件正文的内容,而是直接读取文件系统中的元数据记录区(例如 Linux 上的 Inode 节点或 Windows 上的 NTFS 主文件表 MFT)。这一检索过程是通过操作系统内核的 stat(2) 系统调用完成的。
Python 对该系统调用进行了直接封装。无论调用 os.stat(path) 还是 pathlib.Path(path).stat(),Python 都会向内核发起一次请求,内核随后打包返回一个名为 os.stat_result 的类元组结构体。这个结构体中包含了该文件在文件系统层面的完整元数据。
在 os.stat_result 对象中,有两个核心属性直接对应本文的核心需求:
st_size:文件的字节大小(Byte)。对于普通文件,该值为文件内容的实际字节数;对于空文件,其值为 0。st_mtime:文件的最后修改时间(Modification Time)。该值是一个浮点数,代表从 Unix 纪元(1970年1月1日 00:00:00 UTC)起至修改时刻所流逝的秒数。
了解这一机制非常重要:因为文件大小和修改时间都保存在同一个元数据包中,所以只需发起一次 stat 调用就能同时拿到两项数据,这比分别调用独立方法能够有效减少一半的系统 I/O 开销。
二、使用 os.stat 获取文件属性
os.stat() 是 Python 早期就具备的传统系统接口,直接接收文件路径字符串(或字节串、路径对象),返回对应的属性结构体。
1. 完整示例代码
import os
from datetime import datetime
# 指定需要检查的目标文件路径
target_file = "data_report.csv"
try:
# 执行单次 stat 系统调用
file_stat = os.stat(target_file)
# 1. 获取文件大小(字节数)
size_bytes = file_stat.st_size
# 2. 获取文件修改时间(原始时间戳)
mtime_timestamp = file_stat.st_mtime
# 将浮点时间戳转为本地日期时间对象并格式化
mtime_readable = datetime.fromtimestamp(mtime_timestamp).strftime("%Y-%m-%d %H:%M:%S")
print(f"文件路径: {target_file}")
print(f"文件大小: {size_bytes} 字节")
print(f"修改时间戳: {mtime_timestamp}")
print(f"格式化修改时间: {mtime_readable}")
except FileNotFoundError:
print(f"错误:指定的文件 '{target_file}' 不存在")
except PermissionError:
print(f"错误:没有访问文件 '{target_file}' 的权限")
2. 运行结果示例
文件路径: data_report.csv
文件大小: 14842912 字节
修改时间戳: 1793326757.284912
格式化修改时间: 2026-10-30 10:19:17
在 os.path 模块中,Python 还提供了快捷辅助函数 os.path.getsize(path) 和 os.path.getmtime(path)。需要注意的是,这两个函数在底层各自独立调用了一次 os.stat()。如果你只需要其中某一项属性,使用它们书写很简便;但如果需要同时获取大小与修改时间,分开调用会导致两次磁盘系统调用,此时应当直接使用 os.stat()。
三、使用 pathlib 获取文件属性(现代推荐方式)
Python 3.4 引入了 pathlib 模块,为文件系统路径提供了面向对象的语义抽象。在现代 Python 编程规范中,处理文件和目录路径推荐优先采用 Path 对象。
1. pathlib 完整实现代码
from pathlib import Path
from datetime import datetime
# 构建 Path 对象
file_path = Path("data_report.csv")
if file_path.is_file():
# 调用 Path.stat() 方法获取元数据
info = file_path.stat()
# 读取大小与修改时间
raw_size = info.st_size
raw_mtime = info.st_mtime
# 转换展示格式
formatted_time = datetime.fromtimestamp(raw_mtime).strftime("%Y-%m-%d %H:%M:%S")
print(f"文件名: {file_path.name}")
print(f"原始大小: {raw_size} Bytes")
print(f"最后修改: {formatted_time}")
else:
print(f"未找到目标文件或该路径非普通文件: {file_path}")
通过 Path.stat() 返回的对象与 os.stat() 完全一致,都是 os.stat_result 实例。其优势在于代码具有高度的面向对象连贯性,不需要频繁在不同的模块和纯字符串之间切分逻辑,能够无缝结合 file_path.exists()、file_path.is_file() 或 file_path.suffix 等属性操作。
四、实用格式化转换技巧
在实际项目和控制台输出中,裸字节(如 14842912)和浮点时间戳(如 1793326757.28)对用户不够友好。我们需要对这两组原始数据进行标准化处理。
1. 字节大小人性化转换函数
计算机存储常用二进制千进位规则(1 KB = 1024 Bytes)。我们可以编写一个短小且健壮的通用函数,循环对 1024 取商,格式化出带单位(B、KB、MB、GB、TB)的字符串:
def format_file_size(size_in_bytes: int) -> str:
"""将字节数转换为带有合理单位(B, KB, MB, GB, TB)的字符串"""
if size_in_bytes < 0:
raise ValueError("文件大小不能为负数")
units = ["B", "KB", "MB", "GB", "TB"]
size = float(size_in_bytes)
unit_index = 0
while size >= 1024.0 and unit_index < len(units) - 1:
size /= 1024.0
unit_index += 1
if unit_index == 0:
return f"{int(size)} {units[unit_index]}"
return f"{size:.2f} {units[unit_index]}"
# 示例调用
print(format_file_size(0)) # 输出: 0 B
print(format_file_size(1024)) # 输出: 1.00 KB
print(format_file_size(14842912)) # 输出: 14.15 MB
print(format_file_size(5368709120))# 输出: 5.00 GB
2. 时间戳的时区与精度处理
st_mtime 默认返回系统本地时区对应的时间戳。在跨地域服务器或分布式日志系统中,经常需要统一转换为国际标准 UTC 时间:
from datetime import datetime, timezone
mtime_ts = 1793326757.284912
# 1. 转换为本地时区时间
local_dt = datetime.fromtimestamp(mtime_ts)
print("本地时间:", local_dt.strftime("%Y-%m-%d %H:%M:%S"))
# 2. 转换为标准 UTC 时间(带时区标识)
utc_dt = datetime.fromtimestamp(mtime_ts, tz=timezone.utc)
print("UTC时间:", utc_dt.strftime("%Y-%m-%d %H:%M:%S %Z"))
st_mtime 可能会在个别平台上因浮点精度损失极微小的纳秒信息。Python 提供了整型属性 st_mtime_ns,直接以纯整数形式提供纳秒时间戳,确保比较的绝对精确。
五、os.stat 与 pathlib.Path.stat 的对比与选型
虽然两种方法底层都指向相同的内核系统调用,但在代码组织、工程维护性和边界处理上各有侧重。
| 特性 / 维度 | pathlib.Path.stat() | os.stat() | os.path.getsize / getmtime |
|---|---|---|---|
| 代码风格 | 面向对象,支持链式操作与丰富路径方法 | 面向过程,操作字符串路径 | 函数式独立查询 |
| 同时读取两项属性 | 单次系统调用,性能高 | 单次系统调用,性能高 | 发起两次系统调用,I/O 翻倍 |
| 跨平台路径拼接 | 内置斜杠运算符(path / "sub")自动适配 |
需搭配 os.path.join |
需搭配 os.path.join |
| 软链接处理 | 默认穿透;支持 .lstat() |
默认穿透;支持 os.lstat() |
强制穿透,无法直查软链自身 |
| 推荐使用场景 | 现代 Python 应用开发、批量文件处理 | 底层系统运维脚本、追求极致微毫秒开销 | 交互式命令行仅查单一属性 |
六、注意事项与避坑指南
在实际生产环境中检索文件属性时,有三个最容易导致逻辑漏洞或程序崩溃的细节需要特别注意:
1. 符号链接(Symlink)穿透陷阱
默认情况下,无论 os.stat() 还是 Path.stat(),都会跟随(Follow)符号链接。这意味着它们返回的是该符号链接最终指向的真实目标文件的大小与修改时间。
如果目标文件已被删除(形成死链接/断链),调用 stat() 会直接抛出 FileNotFoundError,即使该符号链接文件本身在磁盘上真实存在。如果你需要检测符号链接自身的大小或链接本身的修改时刻,必须改用 os.lstat() 或 Path.lstat()。
2. 目录的 st_size 并不代表文件夹内容总大小
如果对一个文件夹路径执行 stat(),所得到的 st_size 通常是一个很小的值(例如在 Linux ext4 上常为 4096 字节,Windows 上常为 0)。这是因为在操作系统层面,目录本身也是一种特殊文件,其 st_size 只代表存储“该目录下文件名与 Inode 对照表”所占用的元数据块大小,绝不是目录下所有文件的总大小。计算文件夹的真实体积必须遍历其所有子文件并累加。
3. 时间属性混淆:mtime、ctime、atime
st_mtime(Modify Time):文件正文内容最后一次被修改写入的时间。st_atime(Access Time):文件最后一次被打开读取的时间(现代操作系统为提升性能,往往开启了noatime或relatime,该值更新频率受系统挂载参数影响)。st_ctime(Change / Creation Time):在类 Unix(Linux/macOS)系统中,ctime 代表元数据改变时间(如修改文件权限、所有者或硬链接发生变化),并非文件创建时间;在 Windows 系统中,ctime 则被操作系统直接映射为文件创建时间(Creation Time)。跨平台编写代码时切勿将 ctime 一概当作创建时间使用。
七、总结
获取文件大小与最后修改时间是 Python 文件处理中最基础且常见的需求。核心要点可以归纳为:
- 一次调用取全量:通过
Path(path).stat()或os.stat(path)一次性读取os.stat_result结构体,优先通过st_size获取字节数,通过st_mtime获取时间戳,杜绝不必要的重复系统调用。 - 注重展示规范:使用除以 1024 的阶梯换算得到人类友好的容量单位;使用
datetime.fromtimestamp转换时间戳,并在分布式环境中显式声明时区。 - 防范边界异常:任何文件系统操作都可能因竞争条件(文件在检查间隙被并发删除或无权限访问)而失败,务必在外层包裹
FileNotFoundError和PermissionError的防御性处理。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













