【问题标题】:Iterate recursively through current, then parent directory to find a subdirectory递归遍历当前目录,然后是父目录以查找子目录
【发布时间】:2015-04-14 08:38:27
【问题描述】:

我想使用boost::filesystem 来查找某个名称的目录。 搜索的开始应该是当前目录。如果我要找的目录不存在,我需要搜索父目录等等。 应搜索 3 个目录级别。 但是,我的代码似乎确实陷入了循环。 boost 是否提供了更方便的解决方案?

for (boost::filesystem::directory_iterator Itr(boost::filesystem::current_path()); oItr != oEndItr; ++oItr)
{
 if (!boost::filesystem::is_directory(oItr->status()))
        continue;
 if (oItr->path().filename().string() != "DirectoryName")
 {
        if (oItr == oEndItr)
            oItr = boost::filesystem::directory_iterator(oItr->path().parent_path());
        continue;
    }
 //Found the directory!
}

【问题讨论】:

  • oItrEnd 设置oItr 指向子目录后出错。但是,即使您在深入时更新了oEndItr,您的代码也需要跟踪oEndItr 的整个堆栈,以便运行每个嵌套目录循环以完成。因此,您可以创建一个堆栈,甚至更简单的递归函数,它使用“堆栈”,因此您不必创建自己的。

标签: recursion boost siblings c++03 boost-filesystem


【解决方案1】:

您需要一个树递归算法来跟踪已访问过的目录。

您应该为此目的规范化路径或使用(device,inode) 组合来检查访问状态。这是为了避免循环(符号)链接循环或同名的不同拼写。

在我的简单示例中:

for (fs::path current : { ".", "..", "../..", "../../../" }) {
    auto const& sub = recurse(fs::canonical(current));
    if (!sub.empty())
        return sub;
}

这也说明了例如../..../../../ 指的是同一个目录(/)。

演示输出:

mkdir -p haystack/{a..z}/sub/{1..10} haystack/j/sub/9/needle
g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp -lboost_system -lboost_filesystem -o test
cd haystack/k/sub/4 && ../../../../test
FOUND "/tmp/1429002953.62583/haystack/j/sub/9/needle"

Live On ColiruLive On Coliru (c++03)

#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional>
#include <set>
#include <iostream>

namespace fs = boost::filesystem;

fs::path find_directory(std::string const& name) {
    std::set<fs::path> visited;

    std::function<fs::path(fs::path const&)> recurse;
    recurse = [&visited, &name, &recurse](fs::path const& dir) -> fs::path {
        if (visited.insert(dir).second) { // not visited already
            try {
                for (auto& de : boost::make_iterator_range(fs::directory_iterator(dir), {})) {
                    if (fs::is_directory(de))
                    {
                        if (de.path().filename() == name)
                            return de.path();

                        // TODO check accessibility?
                        auto const& sub = recurse(de.path());

                        if (!sub.empty())
                            return sub;
                    }
                }
            } catch(fs::filesystem_error& e) {
                std::cerr << "Error: " << e.what() << "\n";
            }
        }
        return {};
    };

    for (fs::path current : { ".", "..", "../..", "../../../" }) {
        auto const& sub = recurse(fs::canonical(current));
        if (!sub.empty())
            return sub;
    }

    return {};
}

int main() {
    std::cout << "FOUND " << find_directory("needle") << "\n";
}

【讨论】:

  • 非常感谢!但是,您似乎正在使用 C++11 代码和 boost > 1.49。那是对的吗?我既不能使用auto,也不能使用这种语法:{".",".."}
  • @tzippy 我不认为你不能使用这些特性会阻止你学习它们是如何工作的,所以如果需要的话,你可以简单地翻译成 c++03: Live On Coliru。我现在为你重新标记你的问题。
  • 如果你从一开始就告诉我们就好了,@tzippy。
  • 很抱歉没有提供这些信息。 @sehe:只是想指出这一点。实际上从您的回答中学到了很多东西!
【解决方案2】:

为了递归迭代一个目录和子目录,Boost.Filesystem 提供了迭代器 boost::filesystem::recursive_directory_iterator。

#include <iostream>
#include <boost/filesystem/path.hpp>

using namespace boost::filesystem;

int main(int argc, char** argv)
{    
    path full_path(initial_path<path>());
    full_path = system_complete(path(argv[0]));
    path dir = full_path.parent_path();

    for(recursive_directory_iterator it(dir); it != recursive_directory_iterator() ; it++)
    {
        if (it->path().filename().string() == "DirectoryName")
        {
            std::cout << it->path() << std::endl;
        }
    }

    return 0;
}

【讨论】:

    猜你喜欢
    • 2019-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    • 2012-03-10
    • 2013-01-23
    • 1970-01-01
    相关资源
    最近更新 更多