当前位置: 首页 > news >正文

文件属性获取与目录IO操作详解

文件属性获取与目录IO操作详解

一、文件属性获取函数

1.1 函数原型及区别

在Linux系统中,我们可以使用以下三个函数来获取文件的属性信息:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int stat(const char *path, struct stat *buf);    // 通过文件名获取
int fstat(int fd, struct stat *buf);             // 通过文件描述符获取  
int lstat(const char *path, struct stat *buf);   // 获取链接文件本身属性

三个函数的区别对比表:

函数参数类型特点适用场景
stat()文件路径字符串会追踪符号链接获取符号链接指向文件的属性
fstat()文件描述符通过已打开的文件获取属性已打开文件的属性获取
lstat()文件路径字符串不追踪符号链接获取符号链接文件本身的属性

1.2 struct stat 结构体详解

struct stat 结构体包含了文件的完整属性信息:

struct stat {dev_t     st_dev;      // 文件所在设备的IDino_t     st_ino;      // inode编号(文件系统内唯一标识)mode_t    st_mode;     // 文件类型和权限nlink_t   st_nlink;    // 硬链接数量uid_t     st_uid;      // 文件所有者的用户IDgid_t     st_gid;      // 文件所有者的组IDdev_t     st_rdev;     // 特殊设备文件的设备IDoff_t     st_size;     // 文件大小(字节),特殊文件为0blksize_t st_blksize;  // 文件系统I/O的块大小blkcnt_t  st_blocks;   // 分配的512字节块数量time_t    st_atime;    // 最后访问时间time_t    st_mtime;    // 最后修改时间time_t    st_ctime;    // 最后状态改变时间
};

时间字段说明表:

时间字段含义触发条件
st_atime最后访问时间读取文件内容时更新
st_mtime最后修改时间修改文件内容时更新
st_ctime最后状态改变时间修改文件属性(如权限、所有者)时更新

1.3 文件类型判断宏

通过 st_mode 字段可以判断文件类型:

S_ISREG(m)   // 是否为普通文件
S_ISDIR(m)   // 是否为目录文件
S_ISCHR(m)   // 是否为字符设备文件
S_ISBLK(m)   // 是否为块设备文件
S_ISFIFO(m)  // 是否为管道文件
S_ISLNK(m)   // 是否为符号链接文件
S_ISSOCK(m)  // 是否为套接字文件

文件类型判断示例表:

返回值文件类型描述
S_ISREG(st_mode)1普通文件常规数据文件
S_ISDIR(st_mode)1目录文件包含其他文件的目录
S_ISCHR(st_mode)1字符设备如终端设备
S_ISBLK(st_mode)1块设备如磁盘分区
S_ISFIFO(st_mode)1管道文件FIFO特殊文件
S_ISLNK(st_mode)1符号链接指向其他文件的链接
S_ISSOCK(st_mode)1套接字文件用于进程间通信

1.4 代码示例

示例1:获取文件大小
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>int main(int argc, char** argv)
{if(argc < 2){printf("请先传递文件名\n");return -1;}struct stat info;if(stat(argv[1], &info) == 0){printf("文件大小: %ld 字节\n", info.st_size);printf("文件占用的磁盘块数: %ld (每块512字节)\n", info.st_blocks);printf("实际占用空间: %ld 字节\n", info.st_blocks * 512);} else {perror("获取文件信息失败");}return 0;
}
示例2:判断文件类型
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>int main(int argc, char** argv)
{if(argc < 2){printf("请先传递文件名\n");return -1;}struct stat info;if(stat(argv[1], &info) != 0){perror("获取文件信息失败");return -1;}printf("文件信息:\n");printf("- 权限模式: %o\n", info.st_mode & 0777);if(S_ISREG(info.st_mode)){printf("- 类型: 普通文件\n");printf("- 大小: %ld 字节\n", info.st_size);}else if(S_ISDIR(info.st_mode)){printf("- 类型: 目录文件\n");}else if(S_ISCHR(info.st_mode)){printf("- 类型: 字符设备文件\n");}else if(S_ISBLK(info.st_mode)){printf("- 类型: 块设备文件\n");}else if(S_ISFIFO(info.st_mode)){printf("- 类型: 管道文件\n");}else if(S_ISLNK(info.st_mode)){printf("- 类型: 符号链接文件\n");}else if(S_ISSOCK(info.st_mode)){printf("- 类型: 套接字文件\n");}else{printf("- 类型: 未知文件类型\n");}return 0;
}
练习:在普通文件末尾追加内容
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int main(int argc, char *argv[])
{struct stat buf = {0};int ret = 0;FILE *fp = NULL;if (argc < 2){printf("参数太少,请指定文件名\n");return -1;}// 使用lstat避免符号链接追踪ret = lstat(argv[1], &buf);if (ret < 0){printf("获取文件属性失败\n");return -1;}if (S_ISREG(buf.st_mode) != 1){printf("该文件不是普通文件,无法追加内容\n");return -1;}printf("该文件是普通文件,大小为 %ld 字节\n", buf.st_size);fp = fopen(argv[1], "a");if (NULL == fp){perror("打开文件失败");return -1;}fwrite("hehe", 4, 1, fp);fclose(fp);printf("成功在文件末尾追加 'hehe'\n");return 0;
}

二、目录IO操作

2.1 学习目录IO的意义

文件系统层次结构示意图:

根目录 /
├── home/
│   ├── user1/
│   │   ├── document.txt
│   │   └── photo.jpg
│   └── user2/
├── etc/
│   └── config.conf
└── var/└── log/

目录是组织和管理文件的容器,通过目录IO可以:

  • 遍历目录内容
  • 创建/删除目录
  • 管理目录结构
  • 实现文件搜索功能

2.2 访问文件与访问目录的区别

文件系统存储结构对比表:

组件存储内容访问方式
文件实际数据内容读写文件内容
目录文件名和inode号的映射表遍历目录项
inode文件元数据(属性)通过系统调用获取

Linux目录结构示意图:

目录项表 (dentry)
+----------------+-----------------+
| 文件名         | inode编号       |
+----------------+-----------------+
| "file1.txt"    | 12345           |
| "file2.c"      | 12346           |
| "subdir"       | 12347           |
+----------------+-----------------+↓
inode表 (存储文件属性)
+---------+-----------------------+
| inode#  | struct stat 信息      |
+---------+-----------------------+
| 12345   | 大小、权限、时间等     |
| 12346   | 大小、权限、时间等     |
| 12347   | 大小、权限、时间等     |
+---------+-----------------------+

2.3 opendir() - 打开目录

#include <sys/types.h>
#include <dirent.h>DIR *opendir(const char *name);

参数说明:

  • name:要打开的目录路径

返回值:

  • 成功:目录流指针 (DIR *)
  • 失败:NULL

验证opendir是否改变当前目录:

#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, char* argv[])
{if(argc < 2){printf("请指定目录路径\n");return -1;}printf("打开目录前的当前目录: ");system("pwd");DIR *dirfp = opendir(argv[1]);if(dirfp == NULL){perror("opendir error");return -1;}printf("打开目录后的当前目录: ");system("pwd");closedir(dirfp);return 0;
}

结论: opendir() 只打开目录进行读取,不会改变程序的当前工作目录。

2.4 chdir() - 切换工作目录

#include <unistd.h>
int chdir(const char *path);

参数:

  • path:目标路径

返回值:

  • 成功:0
  • 失败:-1
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>int main()
{FILE *fp = NULL;char buf[1024] = {0};printf("切换前的当前目录: ");system("pwd");if (chdir("/home/superman") < 0){perror("切换路径失败");return -1;}printf("切换后的当前目录: ");system("pwd");// 现在打开的是 /home/superman/1.txtfp = fopen("./1.txt", "r");if (NULL == fp){perror("打开文件失败");return -1;}fread(buf, 1024, 1, fp);printf("文件内容: %s\n", buf);fclose(fp);return 0;
}

2.5 readdir() - 读取目录内容

#include <dirent.h>
struct dirent *readdir(DIR *dirp);

struct dirent 结构体:

struct dirent {ino_t d_ino;           // 文件索引号off_t d_off;           // 目录项偏移量unsigned short d_reclen; // 目录项大小unsigned char d_type;    // 文件类型char d_name[256];       // 文件名
};

d_type 文件类型常量表:

常量文件类型描述
DT_REG8普通文件常规数据文件
DT_DIR4目录文件夹
DT_LNK10符号链接软链接文件
DT_CHR2字符设备终端等字符设备
DT_BLK6块设备磁盘等块设备
DT_FIFO1管道FIFO特殊文件
DT_SOCK12套接字进程间通信
DT_UNKNOWN0未知类型无法确定类型

2.6 closedir() - 关闭目录

#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);

2.7 完整示例:遍历目录内容

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>const char* get_file_type(unsigned char d_type) {switch(d_type) {case DT_REG: return "普通文件";case DT_DIR: return "目录";case DT_LNK: return "符号链接";case DT_CHR: return "字符设备";case DT_BLK: return "块设备";case DT_FIFO: return "管道";case DT_SOCK: return "套接字";default: return "未知类型";}
}int main(int argc, char* argv[])
{const char* path = ".";if(argc > 1) {path = argv[1];}struct dirent *entry = NULL;DIR *dir_p = opendir(path);if (NULL == dir_p) {perror("打开目录失败");return -1;}printf("目录 '%s' 的内容:\n", path);printf("%-15s %-10s %-15s %s\n", "索引号", "类型", "大小", "文件名");printf("------------------------------------------------\n");while (1) {entry = readdir(dir_p);if (NULL == entry) {break;}printf("%-15lu %-10s %-15d %s\n",entry->d_ino,get_file_type(entry->d_type),entry->d_reclen,entry->d_name);}closedir(dir_p);return 0;
}

目录遍历流程图:

开始↓
opendir()打开目录↓
readdir()读取第一个目录项↓
┌─→ 是否为NULL? ──→ 是 ──→ closedir()关闭目录 ──→ 结束
│       否
│   处理当前目录项信息
│        ↓
└── readdir()读取下一个目录项

2.8 进阶示例:递归遍历目录树

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>void list_directory(const char* path, int depth) {DIR *dir;struct dirent *entry;struct stat statbuf;char fullpath[1024];if ((dir = opendir(path)) == NULL) {return;}while ((entry = readdir(dir)) != NULL) {// 跳过 . 和 .. 目录if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {continue;}// 构建完整路径snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);// 获取文件信息if (lstat(fullpath, &statbuf) == -1) {continue;}// 缩进显示层次结构for (int i = 0; i < depth; i++) {printf("  ");}if (S_ISDIR(statbuf.st_mode)) {printf("📁 %s/\n", entry->d_name);// 递归遍历子目录list_directory(fullpath, depth + 1);} else if (S_ISLNK(statbuf.st_mode)) {printf("🔗 %s -> 链接文件\n", entry->d_name);} else if (S_ISREG(statbuf.st_mode)) {printf("📄 %s (%ld 字节)\n", entry->d_name, statbuf.st_size);} else {printf("❓ %s\n", entry->d_name);}}closedir(dir);
}int main(int argc, char* argv[]) {const char* path = ".";if (argc > 1) {path = argv[1];}printf("目录树: %s\n", path);printf("====================\n");list_directory(path, 0);return 0;
}

三、总结

文件属性获取与目录IO对比表:

功能文件操作目录操作
打开open(), fopen()opendir()
读取read(), fread()readdir()
关闭close(), fclose()closedir()
定位lseek(), fseek()自动顺序读取
信息获取stat(), fstat()readdir()返回dirent

关键知识点:

  1. stat vs lstat:处理符号链接时的区别很重要
  2. 目录流:目录读取是顺序的,类似文件流但结构不同
  3. 文件类型判断:可以通过st_mode宏或d_type字段
  4. 错误处理:所有系统调用都应检查返回值
  5. 资源释放:打开的目录必须用closedir()关闭
http://www.dtcms.com/a/573944.html

相关文章:

  • 优秀网站首页广东省建设注册中心网站
  • 要将ITP集成到Jenkins Pipeline中,实现开发发版时自动触发自动化测试
  • Linux 定时监测 Java 服务
  • 体外产品的研发网站如何建设paypal网站做外贸
  • 浙江城乡建设局和住建局seo课程培训入门
  • 3系统需求调研项目整合管理
  • Nestjs框架: Consul健康检查与gRPC客户端动态管理优化方案
  • 开机自动启动activity
  • 医学图像分割评价指标Dice与HD95的详解
  • 杀毒软件杀毒原理(草稿)
  • 网站开发需要会的东西网页设计大赛主题
  • 如何将iPhone上的笔记传输到电脑
  • 发布公司信息的网站网推接单
  • MES 离散制造核心流程详解(含关键动作、角色与异常处理)
  • 网站建设方案与报价wordpress文章怎么生成标签
  • 雄安投资建设集团网站东莞网站建设咨询
  • ruoyi前端(vue3)框架,切换菜单白屏问题
  • HTML5+CSS3小实例:不用JS实现幽灵网格动画
  • 人工智能 机器学习 深度学习
  • 用C++从零开始实现的小型深度学习训练框架
  • 算法题(Python)数组篇 | 3.有序数组的平方
  • 网站引流怎么做广告海报图片
  • 专业人士怎样建网站超星网站开发实战答案
  • 做推广的网站那个好网路营销网站策划书
  • muduo库学习(1):Reactor模型的设计思想
  • 分布式ID|从源码角度深度解析美团Leaf双Buffer优化方案
  • 【UE·优化篇】使用FramePro优化UI性能
  • 网站建设个人工作室seo在线培训机构
  • 免费做网站怎么做网站吗上海做网站比较好的
  • 【观察者模式】深入 Spring 事件驱动模型:从入门到微服务整合实战