标准IO:目录操作

标准IO:目录操作

1. 概述

  • 背景:在 Linux I/O 课程中,之前主要学习的是针对文件(设备文件、文本文件)的操作。但在实际场景中,经常需要访问目录(Directory),查看目录下的文件列表。
  • 需求:需要专门的接口来处理目录的打开、读取和关闭操作。
  • 核心接口:系统提供了关于目录操作的标准接口,主要涉及 opendirreaddirclosedir 等函数。

2. 打开目录 (opendir)

  • 函数原型DIR *opendir(const char *name);
  • 功能:打开指定路径的目录,返回一个目录流指针。
  • 参数
    • name:目录路径。
    • 建议:推荐使用绝对路径,更加严谨和方便;也可以使用相对路径(取决于程序运行场合)。
    • 注意路径字符串的大小写。
  • 返回值
    • 成功:返回 DIR * 指针(目录流指针)。
    • 失败:返回 NULL
  • 相关接口
    • fdopendir:基于已有的文件描述符(FD)打开目录。需先通过 open 获取 FD,再配合此接口获取目录流指针。一般场景下 opendir 更常用。
  • 头文件
    • <dirent.h>
    • <sys/types.h>

3. 目录流结构 (DIR)

  • 定义DIR 是一个用于描述打开目录文件的结构体类型。
  • 特性
    • 内部细节被屏蔽(Opaque Type),用户无法直接看到其完整结构定义。
    • 使用时以指针形式DIR *)存在。
    • 类似于标准 I/O 中的 FILE * 流指针。
  • 用途:通过该指针关联已打开的目录,后续操作(如读取、关闭)均依赖此指针。

4. 读取目录项 (readdir)

  • 函数原型struct dirent *readdir(DIR *dirp);
  • 功能:从目录流中读取下一个目录项。
  • 参数dirp:由 opendir 返回的目录流指针。
  • 返回值
    • 成功:返回指向 struct dirent 结构体的指针。
    • 结束或错误:返回 NULL
  • 读取机制
    • 每次调用仅返回一个目录项(Entry)。
    • 目录内部维护有读取位置指针,每次调用会自动指向下一个项。
    • 若要读取整个目录内容,需配合循环(如 while)多次调用,直到返回 NULL

5. 目录项结构 (struct dirent)

  • 定义:用于存放读取到的目录中某一个项的信息。
  • 主要成员
    • ino_t d_ino:文件索引节点号(Inode number)。
    • off_t d_off:指向下一个目录项的偏移量。
    • unsigned char d_type:文件类型(见下文类型判断)。
    • char d_name[256]:文件名(字符串)。
  • 文件类型判断 (d_type)
    • 通过 d_type 成员可以判断当前项是文件、目录还是其他类型。
    • 常见宏定义(需在程序中判断):
      • DT_DIR:目录(Directory)。
      • DT_REG:普通文件(Regular file)。
      • DT_LNK:符号链接(Symbolic link)。
      • 其他类型(设备文件等)。
    • 注意:仅凭 readdir 只能获取文件名和基本类型,无法获取权限、时间等详细属性(如需实现 ls -l 效果,需结合文件属性获取接口)。

6. 关闭目录 (closedir)

  • 函数原型int closedir(DIR *dirp);
  • 功能:关闭目录流,释放相关资源。
  • 参数dirp:之前由 opendir 打开的目录流指针。
  • 时机:完成目录读取操作后必须调用。

7. 编程实战示例

  • 逻辑流程
    1. 参数检查:判断命令行是否传入目标目录路径。
    2. 打开目录:调用 opendir,检查返回值是否为 NULL
    3. 循环读取:使用 while 循环调用 readdir
    4. 类型判断:通过 d_type 判断是文件还是目录,并进行不同输出。
    5. 关闭目录:循环结束后调用 closedir
  • 代码结构示意
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>

int main(int argc, char *argv[]) {
    DIR *dir = NULL;
    struct dirent *ptr = NULL;

    // 1. 参数检查
    if (argc < 2) {
        printf("Usage: %s <directory_path>\n", argv[0]);
        return -1;
    }

    // 2. 打开目录
    dir = opendir(argv[1]);
    if (dir == NULL) {
        perror("opendir failed");
        return -1;
    }

    // 3. 循环读取
    while ((ptr = readdir(dir)) != NULL) {
        // 4. 类型判断与输出
        if (ptr->d_type == DT_DIR) {
            printf("[DIR]  %s\n", ptr->d_name);
        } else if (ptr->d_type == DT_REG) {
            printf("[FILE] %s\n", ptr->d_name);
        } else {
            printf("[OTHER] %s\n", ptr->d_name);
        }
    }

    // 5. 关闭目录
    closedir(dir);
    return 0;
}

8. 局限性与后续扩展

  • 当前能力
    • 可以列出目录下的文件名。
    • 可以区分基本的文件类型(目录、普通文件等)。
    • 相当于实现了 ls 命令的基础功能。
  • 当前局限
    • 无法获取文件的详细属性(如权限 rwx、所有者、大小、修改时间等)。
    • 无法实现 ls -l 的详细列表效果。
  • 后续学习
    • 需要学习文件属性获取接口(如 stat 系列函数)。
    • 结合目录操作与文件属性操作,才能完整实现类似 ls -l 的功能。
标准 IO:刷新、定位 2026-03-02
文件属性获取 2026-03-03

评论区