谢谢你们的帮助,现在一切正常。我按照@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;
}
}
}
答案 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)函数。它以递归方式扫描文件树,并给它一些处理函数。