C ++中的递归列表文件不会进入所有子目录

时间:2015-12-14 05:17:37

标签: c++ linux list file directory

!!!解决!!!

谢谢你们的帮助,现在一切正常。我按照@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;
        }
    }
}

4 个答案:

答案 0 :(得分:2)

主要问题在于:

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

答案 1 :(得分:1)

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

__files_or_dirs.erase(it);

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

it = __files_or_dirs.erase(it);

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

答案 2 :(得分:1)

对于这一行:

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

请注意,readdir因此list_dir仅返回文件 name ,而不是完整文件路径。所以此时(&*it)->c_str()只有一个文件名(例如&#34; input.txt&#34;),而不是完整路径,所以当你在子目录中的文件上调用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 :(得分:1)

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