2011-02-06 136 views
0

我是C新手,尝试通过当前工作目录中的所有目录/文件递归并输出其信息。我遇到的问题是,我想不出一个好的解决方法,那就是当同一个目录中存在两个文件夹时,路径第二次出现错误。例如,如果dir1和dir2在完成“/ something/dir1”之后处于相同路径中,则路径应该变为“/ something/dir2”,但由于我写入内容的方式而变为“/ something/dir1/dir2”。我以为只是跟踪以前的路径,但我不确定如何在没有每次递归调用的情况下不断重写它的方法。C中的递归目录

更新:我已经修复了原来的错误,并认为我会在这里发布我的新代码。我没有意识到的是opendir(“。”)和changedir(“..”)实际上会将周期转换为完整的当前或前一个路径。至于将type = 8和type = 4语句更改为更易读的S_ISDIR(statbuf.st_mode)和S_ISREG(statbuf.st_mode)语句,在类型语句执行时,它们看起来根本不起作用。不知道语法和我尝试使用它们的方式有什么问题。

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

} 
+1

您好像已经回答了您自己的问题... – 2011-02-06 19:27:29

+1

@Oli:重读。 OP表示目前的代码中存在一个错误。此外,这将是一个非常有用的学习练习,用于学习如何尽可能在递归的每个级别最好地重用现有缓冲区,而不是在堆上分配大量内存(或更糟的是,在堆栈上)。 – 2011-02-06 19:34:25

+0

`type == 8`? `readdir`手册页中的符号常量是有原因的。 – asveikau 2011-02-06 20:55:52

回答

4

不要无谓地推倒重来。如果你是一个unixy系统上,则nftw库函数(在POSIX的XSI选项组中的一部分,这意味着它几乎普遍有售)正是你想要做什么:

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

在另一方面,如果你在做这个学习练习,或者如果你有一个(适度稀少的)实例,其中nftw不适合你的需求(例如,如果你需要同时从多个线程执行目录递归),请继续调试你的解决方案。

1

我会做的第一件事就是改变这样的:

if(type == 4) 

要这样:

if(S_ISDIR(statbuf.st_mode)) 

更多细节见stat manpage

其次,它看起来像你从来没有打电话closedir。你需要释放东西。

第三个...将代码复制粘贴到两个地方,而不是在两个地方复制代码,将这些冗余工作放到同一个函数中将会很有价值。

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