【问题标题】:Recursing Directories in CC中的递归目录
【发布时间】:2011-06-22 09:07:32
【问题描述】:

我是 C 新手,我试图从当前工作目录中递归所有目录/文件并输出它们的信息。我遇到的问题是我想不出一个好的解决方法是,当同一目录中有两个文件夹时,路径第二次构建错误。例如,如果在“/something/dir1”完成后 dir1 和 dir2 在同一路径中,则路径应该变为“/something/dir2”,但由于我写东西的方式而变为“/something/dir1/dir2”。我想只跟踪以前的路径,但如果不不断地在每次递归调用中重写它,我不确定是否有办法做到这一点。

更新:我已经修复了原来的错误,并认为我会在这里发布我的新代码。我不知道的技巧是 opendir(".") 和 changedir("..") 实际上会将周期转换为当前或以前的完整路径。至于将 type = 8 和 type = 4 语句更改为更具可读性的 S_ISDIR(statbuf.st_mode) 和 S_ISREG(statbuf.st_mode) 语句,它们似乎根本不起作用,而 type 语句则起作用。不确定语法和我尝试使用它们的方式有什么问题。

更新 2:我在这里解决了 S_ISDIR/S_ISREG 问题 - How to use S_ISREG() and S_ISDIR() POSIX Macros?

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

void helper(DIR *, struct dirent *, struct stat, char *, int);
void dircheck(DIR *, struct dirent *, struct stat, char *, int);

int main(int argc, char *argv[]){

  DIR *dip;
  struct dirent *dit;
  struct stat statbuf;
  char currentPath[FILENAME_MAX];
  int depth = 0; /*Used to correctly space output*/

  dip = opendir(".");
  getcwd(currentPath, FILENAME_MAX);

  while((dit = readdir(dip)) != NULL){

    /*Skips . and ..*/
    if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
      continue;

    stat(currentPath, &statbuf);

    /*Checks if current item is of the type file (type 8)*/
    if(dit->d_type == 8)
      printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);

    /*Checks if current item is of the type directory (type 4)*/
    if(dit->d_type == 4)
      dircheck(dip, dit, statbuf, currentPath, depth);

  }
  return 0;
}

/*Recursively called helper function*/
void helper(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
  int i = 0;
  dip = opendir(currentPath);

  while((dit = readdir(dip)) != NULL){

    if(strcmp(dit->d_name, ".") == 0 || strcmp(dit->d_name, "..") == 0)
      continue;

    stat(currentPath, &statbuf);

    if(dit->d_type == 8){
      for(i = 0; i < depth; i++)
        printf("    ");
      printf("%s (%d bytes)\n", dit->d_name, (int)statbuf.st_size);
    }

    if(dit->d_type == 4)
      dircheck(dip, dit, statbuf, currentPath, depth);

  }
}

void dircheck(DIR *dip, struct dirent *dit, struct stat statbuf, char currentPath[FILENAME_MAX], int depth){
  int i = 0;

  strcat(currentPath, "/");
  strcat(currentPath, dit->d_name);

  /*If two directories exist at the same levelt the path
    is built wrong and needs to be corrected*/
  if((chdir(currentPath)) == -1){
    chdir("..");
    getcwd(currentPath, FILENAME_MAX);
    strcat(currentPath, "/");
    strcat(currentPath, dit->d_name);

    for(i = 0; i < depth; i++)
      printf ("    ");
    printf("%s (subdirectory)\n", dit->d_name);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth);
  }

  else{
    for(i =0; i < depth; i++)
      printf("    ");
    printf("%s (subdirectory)\n", dit->d_name);
    chdir(currentPath);
    depth++;
    helper(dip, dit, statbuf, currentPath, depth);
  }

}

【问题讨论】:

  • 您似乎已经回答了自己的问题...
  • @Oli:重读。 OP 说当前代码中存在错误。此外,这将是一个非常有用的学习练习,用于学习如何在可能的情况下最好地在每个递归级别重用现有缓冲区,而不是在堆上(或更糟糕的是,在堆栈上)分配大量内存。
  • type == 8? readdir 联机帮助页中的符号常量是有原因的。
  • 抱歉,我说错了。 stat 联机帮助页。特别是你应该使用S_ISDIR(statbuf.st_mode) 来确定某个东西是否是一个目录。
  • 使用d_type(如果可用)而不是调用stat可以使目录遍历速度提高几个数量级。

标签: c recursion directory


【解决方案1】:

我要做的第一件事就是改变这个:

if(type == 4)

到这里:

if(S_ISDIR(statbuf.st_mode))

更多详情请参阅stat manpage

其次,您似乎从未调用过closedir。你需要释放一些东西。

第三...与其将opendir/readdir 代码复制粘贴到两个位置,不如将这些多余的工作放到同一个函数中。

最后.. 在每次递归时在堆栈上分配MAX_PATH 会变得相当大。您可能要考虑使用mallocrealloc。但是正如@R.. 上面提到的,您应该尽可能地重用这些缓冲区。对于大型目录树,这将占用大量空间且成本高昂。

【讨论】:

  • 非常感谢,这些更改很有帮助。有什么想法可以解决这个错误吗?
  • readdir 代码不在单独函数中的原因是由于当前的错误。这是可以在 main 中重新建立的路径,这就是为什么它将适用于第一个路径中的多个目录的原因。当它递归到更深层次时,它就会成为一个问题。考虑到每次进行递归调用时它都会被覆盖,因此尝试存储以前的路径是我无法弄清楚的。
【解决方案2】:

不要不必要地重新发明轮子。如果您使用的是 unixy 系统,nftw 库函数(POSIX 中 XSI 选项组的一部分,意味着它几乎普遍可用)完全可以满足您的需求:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/nftw.html

另一方面,如果您将此作为学习练习,或者如果您有一个(很少见)nftw 不适合您的需求的实例(例如,如果您需要执行目录递归同时来自多个线程)继续调试您的解决方案。

【讨论】:

  • 我更多地把它作为系统调用的学习练习,但感谢您让我知道这个库。如果我以后需要这个功能,我一定会使用它。
猜你喜欢
  • 2016-04-18
  • 2013-02-17
  • 2013-07-01
  • 2014-04-12
  • 2010-12-01
  • 2016-04-25
  • 2016-12-01
  • 2011-11-14
  • 2017-01-11
相关资源
最近更新 更多