【问题标题】:Recursive listing files in C++ doesn't enter all subdirectoriesC ++中的递归列表文件不会进入所有子目录
【发布时间】:2016-03-19 12:45:35
【问题描述】:

!!!解决了!!!

感谢大家的帮助,现在一切正常。我按照@RSahu 的建议对我的代码进行了更改并让它工作。
感谢您的所有意见,我真的很困惑。
致@Basile:我肯定会检查一下,但是对于这段特殊的代码,我不会使用它,因为它看起来太复杂了:) 但是感谢您的建议。



原问题

我正在尝试制作一个 C++ 代码来列出给定目录及其子目录中的所有文件。

快速解释

想法是函数list_dirs(_dir, _files, _current_dir)将从顶层目录开始并将文件放入向量_files,当它找到一个目录时,它将在这个目录上调用自己。如果在子目录中,_current_dir 将被添加到文件名之前,因为我需要知道路径结构(它应该生成 sitemap.xml)。
list_dirs 中有一个对list_dir 的调用,它只返回当前目录中的所有文件,文件和目录之间没有区别。

我的问题

现在代码的作用是列出原始目录中的所有文件,然后列出一个子目录中的所有文件,但跳过所有其他子目录。它会列出它们,但不会列出其中的文件。
更神秘的是,它只列出这个特定目录中的文件,而不是其他目录。我尝试在多个位置运行它,但它从未进入任何其他目录。

提前致谢,请注意我是 C++ 的初学者,所以不要苛刻 ;)
LIST_DIR

int list_dir(const std::string& dir, std::vector<std::string>& files){
    DIR *dp;
    struct dirent *dirp;
    unsigned fileCount = 0;

    if ((dp = opendir(dir.c_str())) == NULL){
        std::cout << "Error opening dir." << std::endl;
    }

    while ((dirp = readdir(dp)) != NULL){
        files.push_back(std::string (dirp->d_name));
        fileCount++;
    }

    closedir(dp);
    return fileCount;
}

和 LIST_DIRS

int list_dirs (const std::string& _dir, std::vector<std::string>& _files, std::string _current_dir){
    std::vector<std::string> __files_or_dirs;

    list_dir(_dir, __files_or_dirs);

    std::vector<std::string>::iterator it = __files_or_dirs.begin();
    struct stat sb;

    while (it != __files_or_dirs.end()){
        if (lstat((&*it)->c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)){
            /* how to do this better? */
            if (*it == "." || *it == ".."){
                __files_or_dirs.erase(it);
                continue;
            }

            /* here it should go into sub-directory */
            list_dirs(_dir + *it, _files, _current_dir + *it);

            __files_or_dirs.erase(it);
        } else {
            if (_current_dir.empty()){
                _files.push_back(*it);
            } else {
                _files.push_back(_current_dir + "/" + *it);
            }
            ++it;
        }
    }
}

【问题讨论】:

    标签: c++ linux list file directory


    【解决方案1】:

    主要问题出在一行:

    if (lstat((&*it)->c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)){
    

    您在调用lstat 时使用了目录条目的名称。当函数处理子目录时,条目名称不代表有效路径。你需要使用类似的东西:

    std::string entry = *it;
    std::string full_path = _dir + "/" + entry;
    if (lstat(full_path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)){
    

    改进建议

    更新list_dir,使其在输出中不包含"."".."。从一开始就排除这些文件对我来说是有意义的。

    int list_dir(const std::string& dir, std::vector<std::string>& files){
       DIR *dp;
       struct dirent *dirp;
       unsigned fileCount = 0;
    
       if ((dp = opendir(dir.c_str())) == NULL){
          std::cout << "Error opening dir." << std::endl;
       }
    
       while ((dirp = readdir(dp)) != NULL){
          std::string entry = dirp->d_name;
          if ( entry == "." or entry == ".." )
          {
             continue;
          }
    
          files.push_back(entry);
          fileCount++;
       }
    
       closedir(dp);
       return fileCount;
    }
    

    list_dirs 中,无需删除_files_or_dirs 中的项目。可以使用for 循环和删除从_files_or_dirs 删除项目的调用来简化代码。

    我不清楚_current_dir 的目的是什么。也许它可以被删除。

    这是该函数的更新版本。 _current_dir 仅用于在递归调用中构造参数的值。

    int list_dirs (const std::string& _dir,
                   std::vector<std::string>& _files, std::string _current_dir){
       std::vector<std::string> __files_or_dirs;
    
       list_dir(_dir, __files_or_dirs);
    
       std::vector<std::string>::iterator it = __files_or_dirs.begin();
       struct stat sb;
    
       for (; it != __files_or_dirs.end() ; ++it){
          std::string entry = *it;
          std::string full_path = _dir + "/" + entry;
    
          if (lstat(full_path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)){
             /* how to do this better? */
    
             /* here it should go into sub-directory */
             list_dirs(full_path, _files, _current_dir + "/" + entry);
    
          } else {
             _files.push_back(full_path);
          }
       }
    }
    

    【讨论】:

      【解决方案2】:

      对于这一行:

         if (lstat((&*it)->c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)){
      

      注意readdirlist_dir 只返回文件名称,而不是完整的文件路径。所以此时(&amp;*it)-&gt;c_str()只有一个文件名(如“input.txt”),没有完整路径,所以当你对子目录下的文件调用lstat时,系统是找不到的。

      要解决此问题,您需要在调用 lstat 之前添加文件路径。比如:

         string fullFileName;
         if (dir.empty()){
             fullFileName = *it;
         } else {
             fullFileName = dir + "/" + *it;
         }
      
         if (lstat(fullFileName.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)){
      

      您可能必须使用_currentDir 而不是dir,具体取决于它们的实际用途(我无法理解您的解释)。

      【讨论】:

        【解决方案3】:

        我不确定您的代码中的所有问题,但我可以告诉您,这一行和另一行类似的行会导致您出现问题:

        __files_or_dirs.erase(it);
        

        当您调用erase 时,您会在擦除点或擦除点之后使迭代器和引用失效,包括 end() 迭代器(参见erase reference)。您正在调用擦除,然后不存储返回的迭代器,然后在此调用之后再次查看它,这不是一件好事。您至少应该将行更改为此,以便捕获返回的迭代器,该迭代器应指向已擦除元素之后的元素(或 end(),如果它是最后一个元素)

        it = __files_or_dirs.erase(it);
        

        从您发布的代码中还可以看出,_dir_current_dir 之间存在冗余。您不修改其中任何一个。您将它们作为相同的值传递,并且它们在整个函数执行过程中保持相同的值。除非这是简化代码并且您正在做其他事情,否则我建议您删除_current_dir 并坚持使用_dir。您可以将 while 循环中的行替换为 _dir 您正在构建文件名的位置,您将简化代码,这始终是一件好事。

        【讨论】:

        • 哦,感谢您指出这一点,我相应地更改了我的代码,并将在未来记住这一点。
        • 我无法完全按照您在编辑中的建议进行操作,因为我需要输出向量中列出的子目录,而不是原始目录。这就是我需要_current_dir 参数的原因。如果我使用 onlz _dir 参数,那么原始目录中的所有文件都将具有我只希望用于子目录的 original_dir/filename 格式。
        • @Lukᚊrom - 我明白你在说什么,我没有想到你对函数的初始调用看起来像list_dirs("somedir", files, ""); 所以_current_dir 不等于dir。我会划掉我的编辑(抱歉,我来晚了,我一直没有想到)。
        【解决方案4】:

        在 Linux 上更简单的方法是使用 nftw(3) 函数。它递归地扫描文件树,你给它一些处理函数。

        【讨论】:

          猜你喜欢
          • 2011-04-26
          • 1970-01-01
          • 1970-01-01
          • 2010-10-30
          • 2016-07-30
          • 1970-01-01
          • 2010-10-19
          • 1970-01-01
          • 2020-08-19
          相关资源
          最近更新 更多