【问题标题】:Recursively printing the file paths of all files in a directory and its subdirectories递归打印目录及其子目录中所有文件的文件路径
【发布时间】:2017-04-03 11:58:45
【问题描述】:

我正在做一个项目,该项目应该打印出 C 中目录及其所有子目录中所有文件的所有文件路径。基本上,它最终是为了模拟 Linux 中的 find 实用程序。

我有以下代码:

void read_sub(char * sub_dir){
    DIR *sub_dp = opendir(sub_dir);//open a directory stream
    struct dirent * sub_dirp;//define
    struct stat buf;//define a file status structure
    char temp1[]=".";
    char temp2[]="..";
    char temp3[]="/";

    if(sub_dp!=NULL){ //Check if the directory opened successfully 

        while((sub_dirp=readdir(sub_dp))!=NULL){ //until we've read every entry one by one

            char * temp = sub_dirp -> d_name; //get the name 

            if(strcmp(temp, temp1)!=0 && strcmp(temp, temp2)!=0){ //Ignores . and .. in the directory

                char *temp_sub = temp3; // This is '/'
                temp_sub = strcat(temp_sub, temp); // Adds '/' before the name of the entry


                //now you can add the / in front of the entry's name
                char* temp_full_path=malloc(sizeof(char)*2000); //Create a variable to hold the full path

                //Place the passed directory at the front of the path and add the name of the file to the end
                temp_full_path=strcpy(temp_full_path,sub_dir); 
                strcat(temp_full_path,temp_sub);

                //try to open the file path we just created
                DIR * subsubdp = opendir(temp_full_path);


                //if not null, we've found a subdirectory, otherwise it's a file
                if(subsubdp!=NULL){
                        //close the stream because it'll be reopened in the recursive call.
                    closedir(subsubdp);
                    read_sub(temp_full_path);//call the recursive function call.
                }else{
                    printf("%s\n",temp_full_path);
                }
            }
        }//end of while loop
        closedir(sub_dp);//close the stream
    }else{
        printf("cannot open directory\n");
        exit(2);
    }
}   

我通过直接传递“testdir”来运行它,这是一个具有以下结构的目录:

testdir/
|-- dir1
| |-- dir2
| | |-- test5
| | `-- test6
| |-- test3
| `-- test4
|-- dir3
| |-- test7
| `-- test8
|-- test1
`-- test2

因此,它应该输出如下内容:

testdir/dir1/dir2/test5
testdir/dir1/dir2/test6
testdir/dir1/test3
testdir/dir1/test4

等等。然而,实际结果是:

testdir/dir1/dir2/test6
testdir/dir1/dir2/test6test5
testdir/dir1/dir2test3
testdir/dir1/dir2test3test4
testdir/dir1test1
testdir/dir1test1dir3
testdir/dir1test1dir3test2

所以我猜它似乎在运行时可能没有正确清除完整的文件路径?此外,它似乎并没有真正进入 dir3 来打印 test7 和 test8。我做错了什么?谢谢。

【问题讨论】:

  • 第一件事看起来很糟糕,使用strcat()
  • 使用DFS或BFS方式打印一个目录下所有文件和目录的路径。
  • 一如既往,明智的做法是在 POSIX 中使用 *at()-family 文件/目录函数。

标签: c recursion directory traversal


【解决方案1】:

您可以使用S_ISDIR 来测试目录。 malloc 应该用free 清理以避免内存泄漏。在递归函数中实现malloc/free 可能会变得过于复杂,因此最好只声明一个临时缓冲区path[4096]。只要路径名不太长,它就可以工作。如果路径太长,那么最安全的选择是打印错误而不继续。

对于"."".." 你可以声明

const char *temp1 = ".";
const char *temp2 = "..";

这样可以避免意外写入temp1temp2

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

void read_sub(char *subdir)
{
    DIR *dirp = opendir(subdir);
    if (!dirp)
        return;

    struct dirent *dentry;
    while ((dentry = readdir(dirp)) != 0)
    {
        if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
            continue;

        printf("%s/", subdir);

        char path[4096];
        int len = strlen(subdir) + 1 + strlen(dentry->d_name) + 1;
        if (len >= sizeof(path)) 
        {
            printf("path is too long...\n");
            break;
        }
        sprintf(path, "%s/%s", subdir, dentry->d_name);

        struct stat st;
        stat(path, &st);
        if (S_ISDIR(st.st_mode))
        {
            printf("%s\n", dentry->d_name);
            read_sub(path);
        }
        else
        {
            printf("%s\n",dentry->d_name);
        }
    }

    closedir(dirp);
}

或者,您可以为path 使用可变长度数组

int len = strlen(subdir) + 1 + strlen(dentry->d_name) + 1;
char path[len];
sprintf(path, "%s/%s", subdir, dentry->d_name);

【讨论】:

    【解决方案2】:

    您没有为目标字符串分配足够的空间。您至少需要 PATH_MAX 字节。

    试试

    char temp1[PATH_MAX] = ".";
    char temp2[PATH_MAX] = "..";
    char temp3[PATH_MAX] = "/";
    

    实际上,编译器只会分配足够的空间来分别保存初始化字符串 (2, 3, 2)。所以你的程序表现出未定义的行为。我会推荐snprintf() 而不是strcat(),因为它直接而且不那么昂贵。每次调用strcat()时,它都会搜索目标字符串的当前结尾,直到找到终止的'\0'

    另外,尽量使用最少的缩进级别,因为代码很快就会变得非常不可读。

    【讨论】:

      猜你喜欢
      • 2017-03-15
      • 2011-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多