文件 IO、目录操作与文件属性
一、系统调用文件 IO底层操作的核心1.1 系统调用 vs 库函数系统调用Linux 内核直接提供的函数如open/read/write无缓冲区直接操作硬件性能更高但可移植性差。库函数对系统调用的封装如fopen/fread/fwrite自带缓冲区使用更简单、跨平台。本质库函数最终还是会调用系统调用只是帮我们做了缓冲和易用性封装。1.2 核心系统调用函数1. 打开 / 创建文件open#include fcntl.h int open(const char *pathname, int flags, mode_t mode);参数pathname文件路径如test.txt。flags打开模式必须选其一O_RDONLY只读O_WRONLY只写O_RDWR读写附加选项O_APPEND追加写O_CREAT文件不存在则创建O_TRUNC清空文件内容mode文件权限仅在创建文件时有效如0666。返回值成功返回文件描述符 fd非负整数失败返回 - 1 并设置errno。示例创建并打开一个文件int fd open(test.txt, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd -1) { perror(open failed); return -1; }2. 关闭文件close#include unistd.h int close(int fd);功能关闭文件描述符释放内核资源。注意文件描述符是有限资源用完必须关闭。3. 读文件read#include unistd.h ssize_t read(int fd, void *buf, size_t count);功能从fd中读取最多count字节到buf缓冲区。返回值成功返回读取的字节数到达文件末尾返回 0失败返回 - 1。4. 写文件write#include unistd.h ssize_t write(int fd, const void *buf, size_t count);功能将buf中的count字节写入fd。返回值成功返回写入的字节数失败返回 - 1。5. 文件定位lseek#include unistd.h off_t lseek(int fd, off_t offset, int whence);功能移动文件读写指针。whence参考位置SEEK_SET文件开头SEEK_CUR当前位置SEEK_END文件末尾经典用法获取文件大小off_t size lseek(fd, 0, SEEK_END); // 移到末尾返回值就是文件大小1.3 文件权限与umask文件权限由mode ~umask决定umask默认是0002所以0666最终权限是0664。权限表示rwxrwxrwx分别对应用户、组、其他用户的读 / 写 / 执行权限。r4w2x1例rw-rw-r--→664→110 110 100二、目录操作遍历与管理2.1 目录操作核心函数Linux 中目录也是一种文件需要用专门的函数操作函数功能原型opendir打开目录DIR *opendir(const char *name);readdir读取目录项struct dirent *readdir(DIR *dirp);closedir关闭目录int closedir(DIR *dirp);2.2 目录项结构体struct direntstruct dirent { ino_t d_ino; // inode编号 off_t d_off; // 偏移量 unsigned short d_reclen; // 记录长度 unsigned char d_type; // 文件类型 char d_name[256]; // 文件名 };d_type文件类型常见值DT_REG普通文件DT_DIR目录DT_LNK软链接DT_CHR字符设备DT_BLK块设备2.3 实战遍历目录并统计文件类型#include stdio.h #include dirent.h #include sys/types.h int main(int argc, char *argv[]) { if (argc ! 2) { printf(Usage: %s dir\n, argv[0]); return -1; } // 1. 打开目录 DIR *dir opendir(argv[1]); if (dir NULL) { perror(opendir failed); return -1; } int dir_cnt 0, file_cnt 0; struct dirent *entry; // 2. 遍历目录项 while ((entry readdir(dir)) ! NULL) { // 跳过.和.. if (entry-d_name[0] .) continue; if (entry-d_type DT_DIR) { dir_cnt; printf([目录] %s\n, entry-d_name); } else if (entry-d_type DT_REG) { file_cnt; printf([文件] %s\n, entry-d_name); } } printf(目录数: %d, 文件数: %d\n, dir_cnt, file_cnt); closedir(dir); return 0; }三、文件属性获取与解析3.1 获取文件属性stat#include sys/stat.h int stat(const char *pathname, struct stat *statbuf);功能获取文件的详细信息存储到struct stat结构体中。3.2struct stat核心字段struct stat { dev_t st_dev; // 设备ID ino_t st_ino; // inode编号 mode_t st_mode; // 文件类型 权限 nlink_t st_nlink; // 硬链接数 uid_t st_uid; // 所有者ID gid_t st_gid; // 组ID off_t st_size; // 文件大小字节 time_t st_atime; // 最后访问时间 time_t st_mtime; // 最后修改时间 time_t st_ctime; // 最后状态改变时间 };3.3 解析st_mode文件类型与权限st_mode是一个 16 位整数高 4 位表示文件类型低 9 位表示权限。1. 判断文件类型S_ISREG(st_mode) // 普通文件 S_ISDIR(st_mode) // 目录 S_ISLNK(st_mode) // 软链接 S_ISCHR(st_mode) // 字符设备 S_ISBLK(st_mode) // 块设备2. 解析权限权限位对应rwxrwxrwx我们可以用掩码提取// 用户权限 st_mode S_IRUSR // 用户读 st_mode S_IWUSR // 用户写 st_mode S_IXUSR // 用户执行 // 组权限 st_mode S_IRGRP // 组读 st_mode S_IWGRP // 组写 st_mode S_IXGRP // 组执行 // 其他权限 st_mode S_IROTH // 其他读 st_mode S_IWOTH // 其他写 st_mode S_IXOTH // 其他执行3.4 实战实现ls -l的核心功能#include stdio.h #include sys/stat.h #include pwd.h #include grp.h #include time.h void print_file_info(const char *path) { struct stat st; if (stat(path, st) -1) { perror(stat failed); return; } // 1. 文件类型 char type; if (S_ISREG(st.st_mode)) type -; else if (S_ISDIR(st.st_mode)) type d; else if (S_ISLNK(st.st_mode)) type l; else if (S_ISCHR(st.st_mode)) type c; else if (S_ISBLK(st.st_mode)) type b; else type ?; // 2. 权限 char perm[10] ---------; if (st.st_mode S_IRUSR) perm[0] r; if (st.st_mode S_IWUSR) perm[1] w; if (st.st_mode S_IXUSR) perm[2] x; if (st.st_mode S_IRGRP) perm[3] r; if (st.st_mode S_IWGRP) perm[4] w; if (st.st_mode S_IXGRP) perm[5] x; if (st.st_mode S_IROTH) perm[6] r; if (st.st_mode S_IWOTH) perm[7] w; if (st.st_mode S_IXOTH) perm[8] x; // 3. 硬链接数 int nlink st.st_nlink; // 4. 用户名和组名 struct passwd *pw getpwuid(st.st_uid); struct group *gr getgrgid(st.st_gid); // 5. 文件大小 off_t size st.st_size; // 6. 修改时间 char time_buf[20]; strftime(time_buf, sizeof(time_buf), %Y-%m-%d %H:%M, localtime(st.st_mtime)); // 7. 文件名 const char *name strrchr(path, /); name name ? name 1 : path; // 输出 printf(%c%s %d %s %s %5ld %s %s\n, type, perm, nlink, pw-pw_name, gr-gr_name, size, time_buf, name); } int main(int argc, char *argv[]) { if (argc ! 2) { printf(Usage: %s file\n, argv[0]); return -1; } print_file_info(argv[1]); return 0; }四、进阶文件描述符与流的转换在实际开发中我们经常需要在系统 IO 和标准 IO 之间切换fd → FILE*fdopenFILE *fp fdopen(fd, r); // 必须和fd的打开模式对应FILE→ fd*filenoint fd fileno(fp);五、总结与学习建议5.1 核心知识点回顾系统调用 IOopen/close/read/write/lseek直接操作文件描述符无缓冲区。文件权限由mode ~umask决定用rwxrwxrwx表示。目录操作opendir/readdir/closedir遍历目录并处理文件类型。文件属性stat函数获取文件详细信息解析st_mode得到类型和权限。流与 fd 转换fdopen和fileno实现标准 IO 与系统 IO 的互通。