2009-08-10 5 views

Antwort

69

BEARBEITEN: Wie in den Kommentaren erwähnt, ist der folgende Code für Versionen von boost::filesystem vor v3 gültig. Lesen Sie für v3 die Vorschläge in den Kommentaren.


boost::filesystem nicht Wildcard-Suche hat, haben Sie Dateien selbst zu filtern.

Dies ist ein Beispielcode, den Inhalt eines Verzeichnisses mit einer boost::filesystem ‚s Extrahieren directory_iterator und Filtrieren mit boost::regex:

const std::string target_path("/my/directory/"); 
const boost::regex my_filter("somefiles.*\.txt"); 

std::vector<std::string> all_matching_files; 

boost::filesystem::directory_iterator end_itr; // Default ctor yields past-the-end 
for(boost::filesystem::directory_iterator i(target_path); i != end_itr; ++i) 
{ 
    // Skip if not a file 
    if(!boost::filesystem::is_regular_file(i->status())) continue; 

    boost::smatch what; 

    // Skip if no match for V2: 
    if(!boost::regex_match(i->leaf(), what, my_filter)) continue; 
    // For V3: 
    //if(!boost::regex_match(i->path().filename(), what, my_filter)) continue; 

    // File matches, store it 
    all_matching_files.push_back(i->leaf()); 
} 

(Wenn Sie sich für eine ready-to-use-Klasse mit builtin-Verzeichnis Filterung, werfen Sie einen Blick auf Qt QDir.)

+20

danke für die sehr vollständige antwort.zwei Anmerkungen für andere: 1) Leaf ist im Dateisystem v3 veraltet (aktueller Standard), benutze path(). filename() statt 2) Wenn das Filterkriterium die Erweiterung ist (sehr häufig), ist es einfacher, i-> path () .extension() == ".txt" [als Beispiel] als Regex – alfC

+16

leaf() ist jetzt veraltet. i-> leaf() kann ersetzt werden durch i-> path(). string() oder i-> path(). filename(). string() wenn Sie nur die Dateinamen wollen – Fuzz

+4

Der Backslash in Regex muss escaped sein '" Einige Dateien. * \\. txt "' –

1

Ich glaube, das Verzeichnis_iterators wird nur alle Dateien in einem Verzeichnis bereitstellen. Es liegt an Ihnen, sie nach Bedarf zu filtern.

3

Meine Lösung ist im Wesentlichen der gleiche wie Julien-L, aber in der Include-Datei verkapselt ist es schöner zu verwenden. Implementiert mit boost :: Dateisystem v3. Ich nehme an, dass so etwas nicht direkt im boost :: Dateisystem enthalten ist, da es eine Abhängigkeit von boost :: regex erzeugen würde.

#include "FilteredDirectoryIterator.h" 
std::vector<std::string> all_matching_files; 
std::for_each(
     FilteredDirectoryIterator("/my/directory","somefiles.*\.txt"), 
     FilteredDirectoryIterator(), 
     [&all_matching_files](const FilteredDirectoryIterator::value_type &dirEntry){ 
       all_matching_files.push_back(dirEntry.path()); 
      } 
     ); 

alternativ FilteredRecursiveDirectoryIterator für die Suche rekursiv Unterverzeichnisse verwenden:

#include "FilteredDirectoryIterator.h" 
std::vector<std::string> all_matching_files; 
std::for_each(
     FilteredRecursiveDirectoryIterator("/my/directory","somefiles.*\.txt"), 
     FilteredRecursiveDirectoryIterator(), 
     [&all_matching_files](const FilteredRecursiveDirectoryIterator::value_type &dirEntry){ 
       all_matching_files.push_back(dirEntry.path()); 
      } 
     ); 

FilteredDirectoryIterator.h

#ifndef TOOLS_BOOST_FILESYSTEM_FILTEREDDIRECTORYITERATOR_H_ 
#define TOOLS_BOOST_FILESYSTEM_FILTEREDDIRECTORYITERATOR_H_ 

#include "boost/filesystem.hpp" 
#include "boost/regex.hpp" 
#include <functional> 

template <class NonFilteredIterator = boost::filesystem::directory_iterator> 
class FilteredDirectoryIteratorTmpl 
: public std::iterator< 
    std::input_iterator_tag, typename NonFilteredIterator::value_type 
    > 
{ 
private: 
    typedef std::string string; 
    typedef boost::filesystem::path path; 
    typedef 
     std::function< 
      bool(const typename NonFilteredIterator::value_type &dirEntry) 
      > 
     FilterFunction; 

    NonFilteredIterator it; 

    NonFilteredIterator end; 

    const FilterFunction filter; 

public: 

    FilteredDirectoryIteratorTmpl(); 

    FilteredDirectoryIteratorTmpl(
     const path &iteratedDir, const string &regexMask 
     ); 

    FilteredDirectoryIteratorTmpl(
     const path &iteratedDir, const boost::regex &mask 
     ); 

    FilteredDirectoryIteratorTmpl(
     const path &iteratedDir, 
     const FilterFunction &filter 
     ); 

    //preincrement 
    FilteredDirectoryIteratorTmpl<NonFilteredIterator>& operator++() { 
     for(++it;it!=end && !filter(*it);++it); 
     return *this; 
    }; 

    //postincrement 
    FilteredDirectoryIteratorTmpl<NonFilteredIterator> operator++(int) { 
     for(++it;it!=end && !filter(*it);++it); 
     return FilteredDirectoryIteratorTmpl<NonFilteredIterator>(it,filter); 
    }; 
    const boost::filesystem::directory_entry &operator*() {return *it;}; 
    bool operator!=(const FilteredDirectoryIteratorTmpl<NonFilteredIterator>& other) 
    { 
     return it!=other.it; 
    }; 
    bool operator==(const FilteredDirectoryIteratorTmpl<NonFilteredIterator>& other) 
    { 
     return it==other.it; 
    }; 
}; 

typedef 
    FilteredDirectoryIteratorTmpl<boost::filesystem::directory_iterator> 
    FilteredDirectoryIterator; 

typedef 
    FilteredDirectoryIteratorTmpl<boost::filesystem::recursive_directory_iterator> 
    FilteredRecursiveDirectoryIterator; 

template <class NonFilteredIterator> 
FilteredDirectoryIteratorTmpl<NonFilteredIterator>::FilteredDirectoryIteratorTmpl() 
: it(), 
    filter(
     [](const boost::filesystem::directory_entry& /*dirEntry*/){return true;} 
     ) 
{ 

} 

template <class NonFilteredIterator> 
FilteredDirectoryIteratorTmpl<NonFilteredIterator>::FilteredDirectoryIteratorTmpl(
    const path &iteratedDir,const string &regexMask 
    ) 
: FilteredDirectoryIteratorTmpl(iteratedDir, boost::regex(regexMask)) 
{ 
} 

template <class NonFilteredIterator> 
FilteredDirectoryIteratorTmpl<NonFilteredIterator>::FilteredDirectoryIteratorTmpl(
    const path &iteratedDir,const boost::regex &regexMask 
    ) 
: it(NonFilteredIterator(iteratedDir)), 
    filter(
     [regexMask](const boost::filesystem::directory_entry& dirEntry){ 
      using std::endl; 
      // return false to skip dirEntry if no match 
      const string filename = dirEntry.path().filename().native(); 
      return boost::regex_match(filename, regexMask); 
     } 
     ) 
{ 
    if (it!=end && !filter(*it)) ++(*this); 
} 

template <class NonFilteredIterator> 
FilteredDirectoryIteratorTmpl<NonFilteredIterator>::FilteredDirectoryIteratorTmpl(
    const path &iteratedDir, const FilterFunction &filter 
    ) 
: it(NonFilteredIterator(iteratedDir)), 
    filter(filter) 
{ 
    if (it!=end && !filter(*it)) ++(*this); 
} 

#endif 
4

Es gibt eine Boost Range Adaptors Weg ist:

#define BOOST_RANGE_ENABLE_CONCEPT_ASSERT 0 
#include <boost/filesystem.hpp> 
#include <boost/range/adaptors.hpp> 

namespace bfs = boost::filesystem; 
namespace ba = boost::adaptors; 

const std::string target_path("/my/directory/"); 
const boost::regex my_filter("somefiles.*\.txt"); 
boost::smatch what; 

for (auto &entry: boost::make_iterator_range(bfs::directory_iterator(target_path), {}) 
| ba::filtered(static_cast<bool (*)(const bfs::path &)>(&bfs::is_regular_file)) 
| ba::filtered([&](const bfs::path &path){ return boost::regex_match(path.filename().string(), what, my_filter); }) 
) 
{ 
    // There are only files matching defined pattern "somefiles*.txt". 
    std::cout << entry.path().filename() << std::endl; 
} 
+0

Gut eins! Range Adapter zur Rettung. – berkus

+1

Für diejenigen, die heutzutage darauf stoßen: std :: regex_match sowie boost :: regex_match akzeptieren keine temporäre Zeichenfolge mehr. Für das obige Beispiel bedeutet dies, dass der Lambda-Text an etwas wie auto oFile = Pfad, Dateiname() angepasst werden muss. String(); gib xxx :: regex_match zurück (oFile, ...). – gilgamash

0

Ich war auf der Suche nach einer Lösung für dieses früher und ich denke, dass meine Lösung die einfachste ist

#include <boost/filesystem.hpp> 
#include <boost/regex.hpp> 
#include <boost/iterator/iterator_facade.hpp> 
#include <boost/exception/all.hpp> 

struct dir_filter_iter 
     : public boost::iterator_facade< 
       dir_filter_iter, 
       boost::filesystem::path, 
       boost::forward_traversal_tag, 
       boost::filesystem::path 
     > 
{ 
     using path = boost::filesystem::path; 
     using impl_type = boost::filesystem::directory_iterator; 

     dir_filter_iter():impl_(){} 
     dir_filter_iter(path p, boost::regex rgx):impl_(std::move(p)),rgx_(std::move(rgx)){ 
       namespace bf = boost::filesystem; 
       if(! bf::is_directory(p)){ 
         BOOST_THROW_EXCEPTION(
           boost::enable_error_info(std::domain_error("not a dir")) 
           << boost::errinfo_file_name(p.string())); 
       } 
     } 
     private: 
     friend class boost::iterator_core_access; 
     bool equal(const dir_filter_iter& that)const{ 
       return this->impl_ == that.impl_; 
     } 
     void increment(){ 
       assert(impl_ != impl_type()); 
       for(;;){ 
         ++impl_; 
         if(impl_ == impl_type()) 
           break; 
         std::string s(impl_->path().string()); 
         if(boost::regex_match(s, rgx_)){ 
           break; 
         } 
       } 
     } 
     path dereference()const{ 
       assert(impl_ != impl_type()); 
       return *impl_; 
     } 
     impl_type impl_; 
     boost::regex rgx_; 
}; 
struct dir_filter_iter_maker{ 
     using value_type = dir_filter_iter; 

     explicit dir_filter_iter_maker(boost::regex rgx):rgx_(rgx){} 

     value_type make()const{ 
       return value_type(); 
     } 
     value_type make(boost::filesystem::path p)const{ 
       return value_type(std::move(p),rgx_); 
     } 
     template<typename... Args> 
     auto operator()(Args&&... args)->decltype(make(args...)){ 
       return this->make(std::forward<Args>(args)...); 
     } 
     private: 
     boost::regex rgx_; 
}; 

Dann können Sie

dir_filter_iter_maker di_maker(boost::regex(R"_(.*\.hpp)_")); 
    std::for_each(di_maker(p), di_maker(), [](const bf::path& p){std::cout << p.string() << "\n";}); 
1

Die akzeptierte Antwort für mich selbst kompilieren nicht tun, wenn ich i->path().extension() verwendet stattdessen von leaf(). Was für mich funktionierte, war ein Beispiel aus this website. Hier ist der Code, geändert, einen Filter anwenden:

vector<string> results; 
filesystem::path filepath(fullpath_to_file); 
filesystem::directory_iterator it(filepath); 
filesystem::directory_iterator end; 
const boost::regex filter("myfilter(capturing group)"); 
BOOST_FOREACH(filesystem::path const &p, make_pair(it, end)) 
{ 
    if(is_regular_File(p)) 
    { 
      match_results<string::const_iterator> what; 
      if (regex_search(it->path().filename().string(), what, pidFileFilter, match_default)) 
      { 
       string res = what[1]; 
       results.push_back(res); 
      } 
    } 
} 

I-Boost-Version bin mit: 1.53.0.

Warum wir nicht alle nur glob() verwenden und einige Regex ist über mich hinaus.

Verwandte Themen