Рекурсивный вывод списка всех подкаталогов данного каталога - PullRequest
1 голос
/ 05 мая 2020

Мне нужно рекурсивно перечислить все подкаталоги данного каталога, и я получил его работающим, но он перечисляет их не по порядку. Я хочу, чтобы он перечислил все подкаталоги данного каталога, а затем перешел к следующему подкаталогу, и я знаю это, потому что моя рекурсия находится внутри while l oop, но я не смог понять, как это реализовать вне л oop. Я думал о создании массива строк с путями подкаталога и использовании его в качестве стека, но после его поиска я не думаю, что это возможно.

#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;
}

Ответы [ 2 ]

1 голос
/ 05 мая 2020

Мне нужно рекурсивно перечислить все подкаталоги данного каталога

Обратите внимание, что каталоги неизвестны стандарту C11 или предыдущим (некоторые * 1007 В операционных системах * MS-DOS в прошлом веке не было никаких каталогов, но был компилятор C; и VMS имела иное понятие каталога, чем ОС, работающая на вашем компьютере; AFAIK Arduino программируется в C, но обычно не имеет файловой системы вообще). Проверьте, прочитав n1570 . API, относящийся к каталогам: операционная система указывает c. В Windows это будет WinAPI .

В системах Linux вы должны использовать nftw (3) ; он сделает большую часть работы за вас; он является частью GNU glib c или musl lib c, исходный код которых находится в свободном доступе. Или рассмотрите такие библиотеки, как Glib (или POCO или Qt , если вы можете использовать C ++), и изучите их исходный код.

Вы бы прошли на nftw некоторую функцию, которая заполняет (и / или reallo c) некоторый глобальный или static указатель на struct , тщательно определенный вами , возможно, заканчивающийся некоторыми гибкий элемент массива и использовать C динамический c выделение памяти .

Обратите внимание, что GNU findutils - это бесплатное программное обеспечение . Вы можете изучить его исходный код для вдохновения.

Обратите внимание, что sa sh или busybox имеет встроенную команду find и является с открытым исходным кодом . Вы можете изучить его исходный код для вдохновения.

И zsh оболочка (также fish оболочка ) имеет мощные средства расширения оболочки, в том числе рекурсивные подстановка как ls **/*.c, чтобы вы могли изучить его исходный код для вдохновения.

Вы неправильно кодируете :

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

, но это неверно ( неопределенное поведение ), поскольку mallo c может дать сбой, и вы должны проверить это, а вы этого не сделали (помните, что strcat ожидает действительный пункт назначения).

Пожалуйста, прочтите быстро Как отлаживать небольшие программы . На Linux скомпилируйте свой код с gcc -Wall -Wextra -g и научитесь использовать отладчик gdb .

1 голос
/ 05 мая 2020

Чтобы распечатать структуру каталогов в 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 возвращает размер указателей; strlen - это, вероятно, то, что вы намеревались получить, чтобы получить длины строк, на которые указывает указатель. Это позволяет избежать неопределенного поведения из-за возможного выхода strcat за пределы выделенной памяти.
  • Всегда free памяти после использования, чтобы избежать утечек памяти.
  • Компиляция с флагом -Wall для включения warnings on показывает, что элемент управления достигает конца непустой функции. Измените тип возвращаемого значения с int на void, если вы на самом деле не возвращаете целое число.
  • Обратите внимание, что я изменил место появления отпечатков. Вы можете переместить их обратно в место рекурсивного вызова, но я предпочитаю выполнить работу для каждого узла в верхней части функции, прежде чем исследовать дочерние элементы. Алгоритмы в основном те же.
...