2011-02-06 155 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,你應該嘗試在你可以的地方重新使用這些緩衝區。對於大型目錄樹,這將佔用大量空間並且價格昂貴。