递归列出给定目录的所有子目录

Recursively listing all subdirectory of a given directory

我必须递归地列出给定目录的所有子目录并且我让它工作,但它列出它们是乱序的。我希望它列出给定目录的所有子目录,然后移动到下一个子目录,我知道这是因为我的递归在 while 循环内,但我一直无法弄清楚如何在循环外实现它.我想过用子目录的路径制作一个字符串数组并将其用作堆栈,但在查找之后,我认为这是不可能的。

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>

int listDir(char *name)
{
    DIR *dir;
    struct dirent *cDir;

    dir = opendir(name);
    if(dir != NULL)
    {
        while((cDir=readdir(dir)) != NULL)
        {
            char* subName = cDir->d_name;       
            if(strcmp(subName, ".")==0 || strcmp(subName, "..")==0)
                continue;
            else
            {
                //  Checks if it's a directory
                if(cDir->d_type == DT_DIR)        
                {
                    printf("%s\n", subName);
                    char *path;
                    path = malloc(sizeof(name) + sizeof(subName) + 2);
                    strcat(path, name);
                    strcat(path, "/");
                    strcat(path, subName);
                    listDir(path);                  
                }   
            }
        }


        closedir(dir);
    }

}

int main(int argc, char *argv[])
{   
    printf("%s\n", argv[1]);
    listDir(argv[1]);

    return 0;
}

要在 BFS 中打印目录结构,您可以使用队列。由于 C 在其标准库中没有这样的东西,因此您必须自己动手或使用库。这是一个简单的例子,说明它是如何工作的,还有你的 DFS 的清理版本。

#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void listDirBFS(char *name)
{
    int q_front = 0;
    int q_back = 1;
    int q_cap = 4;
    char **q = malloc(q_cap * sizeof(*q));

    for (q[0] = strdup(name); q_front != q_back;) 
    {
        name = q[q_front++];
        DIR *dir = opendir(name);

        if (!dir) continue;

        printf("%s\n", name);
        size_t name_len = strlen(name);
        struct dirent *cDir;

        while ((cDir = readdir(dir)))
        {
            char *subName = cDir->d_name;

            if (strcmp(subName, ".") && strcmp(subName, "..") &&
                cDir->d_type == DT_DIR)
            {
                char *path = malloc(name_len + strlen(subName) + 2);
                sprintf(path, "%s/%s", name, subName);

                if (q_back >= q_cap && 
                    !(q = realloc(q, sizeof(*q) * (q_cap *= 2)))) 
                {
                    fprintf(stderr, "%s:%d realloc\n", __FILE__, __LINE__);
                    exit(1);
                }

                q[q_back++] = path;
            }
        }

        free(name);         
        closedir(dir);
    }

    free(q);
}

void listDir(char *name)
{
    DIR *dir = opendir(name);

    if (!dir) return;

    printf("%s\n", name);
    size_t name_len = strlen(name);
    struct dirent *cDir;

    while ((cDir = readdir(dir)))
    {
        char *subName = cDir->d_name;

        if (strcmp(subName, ".") && strcmp(subName, "..") &&
            cDir->d_type == DT_DIR)
        {
            char *path = malloc(name_len + strlen(subName) + 2);
            sprintf(path, "%s/%s", name, subName);
            listDir(path);
            free(path);
        }
    }

    closedir(dir);
}

int main(int argc, char **argv)
{   
    puts("== DFS ==");
    listDir(".");
    puts("\n== BFS ==");
    listDirBFS(".");
    return 0;
}

输出:

== DFS ==
.
./a
./a/b
./a/b/e
./a/bb
./a/bb/f
./a/bb/f/g
./aa
./aa/c
./aaa
./aaa/d
./aaa/d/h
./aaa/d/hh

== BFS ==
.
./a
./aa
./aaa
./a/b
./a/bb
./aa/c
./aaa/d
./a/b/e
./a/bb/f
./aaa/d/h
./aaa/d/hh
./a/bb/f/g

对您的代码的几点意见和建议:

  • sizeof(name) + sizeof(subName) 不正确。 sizeof returns 指针的大小; strlen 可能是您想要获取指向的字符串的长度。这避免了由于可能 strcat-ing 超出分配的内存而导致的未定义行为。
  • 使用后始终 free 内存以避免内存泄漏。
  • 使用 -Wall 标志编译以打开警告显示控制到达非空函数的末尾。如果您实际上 return 不是整数,请将 return 类型从 int 更改为 void
  • 请注意,我更改了打印位置。您可以将它们移回递归调用的位置,但我更喜欢在探索子节点之前在函数顶部为每个节点完成工作。算法基本相同。

I have to recursively list all the subdirectories of a given directory

请注意directories are unknown to the C11 standard or previous ones (some MS-DOS operating systems in the previous century did not have any directories, but did had a C compiler; and VMS had a different notion of directory than the OS running on your computer; AFAIK an Arduino is programmable in C but usually has no file system at all). Check by reading n1570. The API related to directories is operating system specific. On Windows it would be the WinAPI.

在 Linux 系统上,如果你会使用 C++,你应该使用 nftw(3); it will do most of the work for you; it is part of GNU glibc or of musl libc whose source code is freely available. Or consider libraries such as Glib (or POCO or Qt)并研究它们的源代码。

您将传递给 nftw 一些填充 (and/or realloc) some global or static pointer to a struct carefully defined by you, perhaps ending with some flexible array member and use C dynamic memory allocation.

的函数

注意 GNU findutils is free software。您可以研究其源代码以获得灵感。

注意 sash or busybox has a builtin find command and is open source。您可以研究其源代码以获取灵感。

zsh shell (also the fish shell) has powerful shell expansion facilities, including recursive globbingls **/*.c 一样,因此您可以研究其源代码以获取灵感。

编码不正确:

               path = malloc(sizeof(name) + sizeof(subName) + 2);
               strcat(path, name);

但这是错误的(undefined behavior) since malloc can fail and you should test that and you did not (remember that strcat 需要一个有效的目的地)。

快速阅读How to debug small programs. On Linux, compile your code with gcc -Wall -Wextra -g and learn to use the gdb debugger.