【问题标题】:I have an array declared in a recursive function. How do I sort it?我有一个在递归函数中声明的数组。我该如何排序?
【发布时间】:2019-06-13 23:54:50
【问题描述】:

我在递归函数中声明了一个数组。是否可以在输出之前对其进行排序?我从另一个递归函数得到的大小。

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

【问题讨论】:

  • 这看起来不像 C++
  • 它使用 VLA,所以它不能是 C++(除非它依赖于非标准扩展)?

标签: c recursion directory


【解决方案1】:

警告:files[counter].name=path 保存了一个局部变量地址,并且在每次循环中你都会修改它,所以所有的名字都是一样的,你需要保存一个副本(strdup

对于 listFilesRecursively 的每次调用,您在堆栈中使用超过 1000 个字节,最好不要在堆栈中使用该字符串并直接使用在堆中分配的路径

我没有看到将 filescounters 作为局部变量的兴趣,你把它们弄丢了

提案

#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


在 valgrind 下执行:

==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)

【讨论】:

  • 我想将它们保存在一个结构数组中......我的结构就像typedef struct file{ char *name; struct stat file_info; }file; 但是当我在函数之外声明这个数组时,我得到分段错误......因为我需要数据不仅要列出,而且要进行操作,例如排序、传输……谢谢您的回答!
  • @Nick 好的,我编辑了我的答案,进行了一些小改动。警告 文件 必须在堆中分配,因为它会在需要时重新分配
  • @Nick 我不使用固定大小来保存路径名(例如,没有char name[256];),以确保我可以保存它的任何长度,并且不会在任何时候消耗内存它比保留的大小要短。 files 也是如此,我不使用固定大小的向量来管理任意数量的文件,也不会使用一个巨大的向量来白白消耗内存。 NFILES 不是 1 也不是 realloc 每次到达文件时
  • 我已经完成了这个项目,但是我有最后一个问题...我正在尝试将所有找到的文件 rename() 到另一个目录,并且目标应该提供 argv[ 3](必要的要求)但是当我尝试这样做时,它会打印出垃圾,就像几个文件连接在一起,当然不会复制任何东西。 Justname 是对我的原始结构的修改,在该结构中我保留了没有完整路径的文件名。
  • for (int i = 0; i &lt;= index; ++i) { char *dest = argv[3]; strcat(dest, "/"); strcat(dest, files[i].justname); printf("%s : %s : %ld\n", files[i].name, dest , (long) files[i].file_info.st_size); if(rename(files[i].name, dest)==0) printf("Success!\n"); else printf("Failed!/n"); }
【解决方案2】:

你的方法不起作用:

  • 在每个递归级别定义了一个新数组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 强制执行的路径长度限制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    • 2023-04-07
    • 2018-01-20
    • 2011-06-10
    • 1970-01-01
    相关资源
    最近更新 更多