У меня есть массив, объявленный в рекурсивной функции. Как мне это отсортировать? - PullRequest
0 голосов
/ 20 января 2019

У меня есть массив, объявленный внутри рекурсивной функции.Можно ли отсортировать его перед выводом?Размер, который я получаю от другой рекурсивной функции.

void listFilesRecursively(char *basePath, int size) {
    char path[1000];
    struct dirent *dp;
    struct file files[size];
    struct stat buf;
    DIR *dir = opendir(basePath);
    int counter = 0;
    if (!dir) return;
    while ((dp = readdir(dir)) != NULL) {
        if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
            strcpy(path, basePath);
            strcat(path, "/");
            strcat(path, dp->d_name);
            files[counter].name = path;
            stat(path, &buf);
            files[counter].file_info.st_size = buf.st_size;
            printf("%s%s%ld%s\n", files[counter].name, " - ",
                   files[counter].file_info.st_size, "bytes");
            counter++;

            listFilesRecursively(path, size);
        }
    }
    closedir(dir);
}

Ответы [ 2 ]

0 голосов
/ 20 января 2019

Ваш подход не работает:

  • существует новый массив files, определенный на каждом рекурсивном уровне.
  • путь, сохраненный для каждой записи в массиве, одинаков, указатель на локальный массив path определен в функции.

Создание files глобальной переменной возможно, но глобальных переменных следует избегать. Вместо этого вы должны передать указатель на структуру, определенную вне рекурсивной функции, и перераспределить массив внутри этой структуры, когда вы найдете больше записей во время рекурсивного спуска. Копия пути для каждого файла должна быть выделена с strdup. Чтобы ограничить требования к пространству стека, path также может быть частью этой структуры, а длина части каталога передается в рекурсивную функцию.

Вот модифицированная версия:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

struct file {
    char *name;
    struct {
        long st_size;
    } file_info;
};

typedef struct dir_state {
    char path[1024];
    struct file *files;
    int files_size;
    int files_count;
} dir_state;

int listFilesRecursively(dir_state *sp) {
    struct dirent *dp;
    struct stat buf;
    int counter = 0;
    int path_len = strlen(sp->path);
    DIR *dir = opendir(sp->path);
    if (!dir)
        return 0;
    while ((dp = readdir(dir)) != NULL) {
        if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
            snprintf(sp->path + path_len, sizeof(sp->path) - path_len, "/%s", dp->d_name);
            if (sp->files_count == sp->files_size) {
                int new_size = sp->files_size * 3 / 2 + 16;
                struct file *new_p = realloc(sp->files, new_size * sizeof(*new_p));
                if (new_p == NULL)
                    return -1;
                sp->files_size = new_size;
                sp->files = new_p;
            }
            memset(&sp->files[sp->files_count], 0, sizeof(struct file));
            sp->files[sp->files_count].name = strdup(sp->path);
            if (!stat(sp->path, &buf))
                sp->files[sp->files_count].file_info.st_size = buf.st_size;
            printf("%s%s%ld%s\n", sp->files[sp->files_count].name, " - ",
                   sp->files[sp->files_count].file_info.st_size, "bytes");
            sp->files_count++;
            counter++;
            listFilesRecursively(sp);
        }
    }
    closedir(dir);
    sp->path[path_len] = '\0';
    return counter;
}

int cmp_name(const void *a, const void *b) {
    const struct file *aa = a;
    const struct file *bb = b;
    return strcmp(aa->name, bb->name);
}

int cmp_size_name(const void *a, const void *b) {
    const struct file *aa = a;
    const struct file *bb = b;
    if (aa->file_info.st_size < bb->file_info.st_size)
        return -1;
    if (aa->file_info.st_size > bb->file_info.st_size)
        return +1;
    return strcmp(aa->name, bb->name);
}

int main(int argc, char *argv[]) {
    dir_state ds = { "", NULL, 0, 0 };
    int i;

    if (argc < 2) {
        strcpy(ds.path, ".");
        listFilesRecursively(&ds);
    } else {
        for (i = 1; i < argc; i++) {
            strcpy(ds.path, argv[i]);
            listFilesRecursively(&ds);
        }
    }
    printf("\nFiles sorted by name:\n");
    qsort(ds.files, ds.files_count, sizeof(*ds.files), cmp_name);
    for (i = 0; i < ds.files_count; i++) {
        printf("%10ld  %s\n", ds.files[i].file_info.st_size, ds.files[i].name);
    }
    printf("\nFiles sorted by size and name:\n");
    qsort(ds.files, ds.files_count, sizeof(*ds.files), cmp_size_name);
    for (i = 0; i < ds.files_count; i++) {
        printf("%10ld  %s\n", ds.files[i].file_info.st_size, ds.files[i].name);
    }
    for (i = 0; i < ds.files_count; i++) {
        free(ds.files[i].name);
    }
    free(ds.files);
    return 0;
}

Примечания:

  • максимальная глубина не ограничена: так как этот метод следует по символическим ссылкам, в дереве каталогов могут быть циклы, приводящие к тому, что одни и те же пути обходятся несколько раз. Однако это не приведет к бесконечной рекурсии благодаря ограничению длины пути, установленному snprintf.
0 голосов
/ 20 января 2019

Предупреждение: files[counter].name=path сохраняет адрес локальной переменной, и при каждом цикле вы изменяете его, поэтому все имена будут одинаковыми, вам нужно сохранить его дубликат ( strdup )

Для каждого вызова listFilesRecursively вы используете более 1000 байтов в стеке, лучше не использовать эту строку в стеке и напрямую работать с путем, выделенным в куче

Я не вижу интереса иметь файлы и счетчики в качестве локальной переменной, вы теряете их, выходя

Предложение

#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#define NFILES 100;

typedef struct file  {
  char * name;
  struct stat file_info;
} file;

void listFilesRecursively(char *basePath, file ** files, int * size, int * index) 
{
  DIR *dir = opendir(basePath);

  if (!dir) 
    return;

  struct dirent *dp;
  struct stat buf;

  while ((dp = readdir(dir)) != NULL)
  {
    if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
    {
        size_t sz = strlen(basePath);

        char * pathname = malloc(sz + strlen(dp->d_name) + 2);

        if (pathname == NULL) {
          /* out of memory */
          closedir(dir);
          return;
        }

        strcpy(pathname, basePath);
        pathname[sz] = '/';
        strcpy(pathname + sz + 1, dp->d_name);

        stat(pathname, &buf);

        if (S_ISDIR(buf.st_mode)) {
          /* suppose dirs not memorized */
          listFilesRecursively(pathname, files, size, index);
          free(pathname);
        }
        else if (S_ISREG(buf.st_mode)) {
          /* a file, memorize it */
          if (++*index == *size) {
            *size += NFILES;
            *files = realloc(*files, (*size) * sizeof(file));
          }

          (*files)[*index].file_info = buf;
          (*files)[*index].name = pathname;
        }
        else
          /* bypassed */
          free(pathname);
    }
  }

  closedir(dir);
}

int compPathname(const void * a, const void * b)
{
  return strcmp(((file *) a)->name, ((file *) b)->name);
}

int main()
{
  int size = NFILES;
  int index = -1;
  file * files = malloc(size * sizeof(file));

  listFilesRecursively(".", &files, &size, &index);

  if (index != -1) {
    qsort(files, index + 1, sizeof(file), compPathname);

    /* write and free memory */
    for (int i = 0; i <= index; ++i) {
      printf("%s : %ld\n", files[i].name, (long) files[i].file_info.st_size);
      free(files[i].name);
    }
  }

  free(files);

  return 0;
}

Я запоминаю только путь и размер обычных файлов, каталоги, динамические ссылки и т. Д. Не сохраняются

сортирую по пути

Я добавляю NFILES каждый раз, когда файлы слишком малы, NFILES может быть любым числом> 0


Казнь под Вальгриндом:

==9329== Memcheck, a memory error detector
==9329== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9329== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==9329== Command: ./a.out
==9329== 
./.X0-lock : 11
./a.out : 12920
./f.c : 2485
./vgdb-pipe-shared-mem-vgdb-9329-by-pi-on-??? : 36
==9329== 
==9329== HEAP SUMMARY:
==9329==     in use at exit: 0 bytes in 0 blocks
==9329==   total heap usage: 35 allocs, 35 frees, 339,242 bytes allocated
==9329== 
==9329== All heap blocks were freed -- no leaks are possible
==9329== 
==9329== For counts of detected and suppressed errors, rerun with: -v
==9329== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...