2015-04-14 12 views
2

Я хочу использовать boost::filesystem, чтобы найти каталог определенного имени. Начало поиска - это текущий каталог. Если каталог, который я ищу, не существует, мне нужно найти родительский каталог и так далее. Необходимо искать 3 уровня каталогов. Однако мой код, похоже, застревает в цикле. Может ли повышение предложить более удобное решение?Итерации рекурсивно через текущий, затем родительский каталог, чтобы найти подкаталог

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! 
} 
+0

'oItrEnd' ошибочно после установки' oItr', чтобы указать на подкаталог. Но даже если вы обновили 'oEndItr', когда вы идете глубже, ваш код должен отслеживать весь * стек *' OEndItr', чтобы завершить цикл каждого вложенного каталога. Таким образом, вы можете создать стек или даже проще, рекурсивную функцию, которая использует «стек», поэтому вам не нужно создавать свои собственные. – VoidStar

ответ

2

Вам нужен рекурсивный алгоритм дерева, который отслеживает, какие каталоги уже были посещены.

Вы должны либо канонировать пути для этой цели, либо использовать комбинацию (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 Coliru или Live 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"; 
} 
+1

Большое спасибо! Однако, похоже, вы используете код C++ 11 и boost> 1.49. Это верно? Я также не могу использовать 'auto' и этот синтаксис:' {".", ".."} ' – tzippy

+0

@tzippy Я не думаю, что тот факт, что вы не можете использовать функции, должен помешать вам узнать, как они работают, поэтому вы можете тривиально перевести на C++ 03, если это необходимо: ** [Live On Coliru] (http://coliru.stacked-crooked.com/a/cd963d67cbda5291) **. Я сейчас снова помету ваш вопрос. – sehe

+0

Было бы хорошо, если бы вы сказали нам, что с самого начала, @tzippy. –

1

Чтобы рекурсивно перебрать каталог и подкаталоги, Boost.Filesystem обеспечивает повышение итератора :: файловая система :: recursive_directory_i terator.

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