20、文件属性获取:使用 stat 函数获取文件大小、修改时间等属性
咱们写 C 程序的时候,经常需要跟文件打交道。读写文件是基本功,但有时候你还得知道这个文件到底多大、什么时候改的、是谁的。这些信息,就是文件的「属性」。
我刚开始做嵌入式开发那会儿,有个需求是要监控日志文件的大小,超过 10MB 就自动备份。当时我第一反应是打开文件,读到末尾算字节数。后来被老同事一句话点醒了:「你直接用 stat 啊,一行代码的事。」嗯,从那以后我就记住了,stat 是获取文件属性的标准手段。
stat 函数是什么
stat 是 POSIX 标准下的系统调用。它的作用很简单:给定一个文件路径,返回这个文件的各种元信息。这些信息包括:
- 文件大小(字节数)
- 最后修改时间
- 最后访问时间
- 文件权限(读、写、执行)
- 文件类型(普通文件、目录、符号链接等)
- 所属用户 ID 和组 ID
- 硬链接数
说白了,你平时在终端里用 ls -l 看到的那一堆信息,底层就是调用了 stat。
函数原型与头文件
使用 stat 之前,记得包含头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
函数原型长这样:
int stat(const char *path, struct stat *buf);
参数很简单:
path:文件路径,字符串buf:指向struct stat结构体的指针,函数会把结果填进去
返回值:成功返回 0,失败返回 -1 并设置 errno。
struct stat 结构体
这个结构体是核心。我挑几个最常用的字段说一下:
| 字段 | 类型 | 说明 |
|---|---|---|
st_size |
off_t |
文件大小,单位字节 |
st_mode |
mode_t |
文件类型 + 权限位 |
st_mtime |
time_t |
最后修改时间 |
st_atime |
time_t |
最后访问时间 |
st_ctime |
time_t |
状态改变时间(注意不是创建时间) |
st_uid |
uid_t |
文件所有者的用户 ID |
st_gid |
gid_t |
文件所属组 ID |
st_nlink |
nlink_t |
硬链接数量 |
小提示:st_ctime 不是创建时间,是「状态改变时间」。比如你改了文件权限,st_ctime 就会更新。很多新手在这里踩过坑,包括我。
完整示例:获取文件大小和修改时间
光说不练假把式。咱们直接写一段代码,看看怎么用 stat 拿到文件大小和最后修改时间:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "用法: %s <文件名>\n", argv[0]);
return 1;
}
struct stat file_stat;
if (stat(argv[1], &file_stat) == -1) {
perror("stat 失败");
return 1;
}
// 文件大小
printf("文件大小: %lld 字节\n", (long long)file_stat.st_size);
// 修改时间
char time_buf[64];
struct tm *tm_info = localtime(&file_stat.st_mtime);
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
printf("最后修改时间: %s\n", time_buf);
// 文件权限(简单打印八进制)
printf("文件权限: %o\n", file_stat.st_mode & 0777);
return 0;
}
编译运行:
gcc -o file_stat file_stat.c
./file_stat test.txt
输出大概长这样:
文件大小: 1024 字节
最后修改时间: 2025-03-15 14:30:22
文件权限: 644
判断文件类型
st_mode 字段里不光有权限,还藏着文件类型。标准库提供了一组宏来判断:
S_ISREG(m)— 普通文件S_ISDIR(m)— 目录S_ISLNK(m)— 符号链接S_ISCHR(m)— 字符设备S_ISBLK(m)— 块设备S_ISFIFO(m)— 命名管道S_ISSOCK(m)— 套接字
举个例子:
if (S_ISREG(file_stat.st_mode)) {
printf("这是一个普通文件\n");
} else if (S_ISDIR(file_stat.st_mode)) {
printf("这是一个目录\n");
}
注意:stat 会跟随符号链接。如果你想知道符号链接本身的属性,要用 lstat 而不是 stat。我曾经在写备份脚本时没注意这个,结果统计的全是目标文件的大小,而不是链接文件本身。
知识体系图
下面这张图帮你理清 stat 相关的核心逻辑:
避坑指南
我这些年用 stat 踩过几个坑,分享给你:
- 文件不存在时 stat 会返回 -1。别假设文件一定存在,记得检查返回值。
- st_size 的类型是 off_t,在 32 位系统上可能只有 4 字节,大文件会溢出。打印时建议强转成
long long。 - 时间字段是 time_t,也就是从 1970 年 1 月 1 日开始的秒数。直接打印是看不懂的,必须用
localtime或gmtime转换。 - 权限判断别自己算位运算,用
S_IRUSR、S_IWUSR这些宏更清晰。
核心总结:stat 是获取文件属性的标准接口。记住三个关键字段:st_size(大小)、st_mtime(修改时间)、st_mode(类型+权限)。配合 S_ISREG 等宏和 localtime 函数,你就能轻松拿到文件的各种信息。