Linux系统编程之目录遍历

概述

目录遍历是指以递归或循环方式,逐个访问文件系统中的文件和子目录,这通常用于实现文件搜索、备份工具、同步服务等功能。为了有效地遍历目录,Linux提供了多种方法,最常见的是:readdir和scandir。下面,我们将分别进行介绍。

readdir

readdir函数通常与opendir、closedir结合使用,以遍历某个目录中的所有条目。每次调用readdir时,会返回下一个条目,直到没有更多条目为止。这三个函数均在头文件"dirent.h"中,其原型如下。

cpp 复制代码
DIR *opendir(const char *name);

int closedir(DIR *dirp);

struct dirent *readdir(DIR *dirp);

opendir函数各个参数和返回值的含义如下。

name: 指向要打开的目录路径名的指针。可以是绝对路径(从根 / 开始)或相对路径(相对于当前工作目录)。

返回值:成功时返回指向DIR结构的指针,表示新打开的目录流。失败时返回NULL,并设置errno来指示具体的错误类型。

closedir函数各个参数和返回值的含义如下。

dirp: 由opendir函数返回的目录流指针。

返回值:成功时返回0,失败时返回-1,并设置errno来指示具体的错误类型。

readdir函数各个参数和返回值的含义如下。

dirp: 由opendir函数返回的目录流指针。

返回值:成功时返回指向struct dirent的指针,包含当前条目的信息。失败时返回NULL,并设置errno来指示具体的错误类型。

在下面的示例代码中,我们首先定义了一个list_dir函数。该函数接收一个路径作为参数,尝试打开该路径所指向的目录,并逐个读取目录中的条目,将每个条目的名称打印出来。main函数会检查命令行参数以确定要列出的目录,默认情况下是当前工作目录,最后调用list_dir函数执行目录遍历任务。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>

void list_dir(const char *path)
{
    // 打开目录
    DIR *pDir = opendir(path);
    if (pDir == NULL)
    {
        printf("open directory failed: %s\n", strerror(errno));
        return;
    }

    // 读取并列出目录内容
    struct dirent *pEntry = NULL;
    while ((pEntry = readdir(pDir)) != NULL)
    {
        printf("%s\n", pEntry->d_name);
    }

    // 关闭目录
    closedir(pDir);
}

int main(int argc, char *argv[])
{
    const char* pszDirPath = (argc > 1) ? argv[1] : ".";
    list_dir(pszDirPath);
    return 0;
}

scandir

scandir函数允许我们一次性读取目录中的所有条目,并且可以根据指定的条件进行筛选和排序。其函数原型如下。

cpp 复制代码
int scandir(const char *dirp, struct dirent ***namelist,
    int (*filter)(const struct dirent *),
    int (*compar)(const struct dirent **, const struct dirent **));

dirp:指向要扫描的目录路径名的指针。

namelist:指向一个指针数组的指针,用来存放返回的条目,每个元素是指向struct dirent的指针。

filter:可选的过滤函数,用于筛选哪些条目应该被包含。如果为NULL,则不过滤。

compar:可选的比较函数,用于对结果进行排序。如果为NULL,则不排序。

返回值:成功时返回匹配条目的数量,失败时返回-1,并设置errno来指示具体的错误类型。

在下面的示例代码中,我们首先定义了一个过滤函数filter,该函数仅允许普通文件通过。在主函数中,我们调用scandir函数,传入目录路径、用于存储结果的指针数组namelist、过滤函数和排序函数。如果成功执行,我们就遍历返回的条目列表,逐个打印文件名,并释放每个条目以及整个列表分配的内存,确保没有内存泄漏。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <fnmatch.h>
#include <errno.h>
#include <string.h>

int filter(const struct dirent *entry)
{
    return entry->d_type == DT_REG;
}

int main()
{
    const char* dirPath = "./";
    struct dirent **namelist;
    // 使用filter函数筛选出普通文件,并按字母顺序排序
    int n = scandir(dirPath, &namelist, filter, alphasort);
    if (n < 0)
    {
        printf("scandir failed: %s\n", strerror(errno));
        return 1;
    }
    else
    {
        for (int i = 0; i < n; ++i)
        {
            printf("%s\n", namelist[i]->d_name);
            // 分配的内存需要手动释放
            free(namelist[i]);
        }
        
        // 释放namelist数组本身
        free(namelist);
    }

    return 0;
}

总结

可以看到,readdir和scandir都可以用于读取目录内容。但它们的工作方式和使用场景有所不同,主要可以归纳为以下几点。

1、使用场景。

readdir:逐个读取目录条目,适用于简单的遍历操作。

scandir:一次性读取所有条目到内存中,对于需要筛选或排序的情况更加高效,因为它减少了重复访问磁盘的需求。

2、灵活性。

readdir:较为简单,适合基本需求。

scandir:提供了更多的控制选项,比如过滤和排序功能。

3、资源管理。

readdir:需要使用closedir及时关闭目录流。

scandir:必须记得释放分配给namelist的内存,以避免内存泄漏。

相关推荐
HLC++10 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室11 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy14 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕15 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫16 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生16 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1117 小时前
linux基础IO
linux·运维·服务器
BelongPanda19 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯19 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983420 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器