Can I use a mask to iterate files in a directory with Boost?

后端 未结 7 1057
既然无缘
既然无缘 2020-11-27 03:51

I want to iterate over all files in a directory matching something like somefiles*.txt.

Does boost::filesystem have something built in to

相关标签:
7条回答
  • 2020-11-27 04:08

    My solution is essentially the same as Julien-L, but encapsulated in the include file it is nicer to use. Implemented using boost::filesystem v3. I guess that something like that is not included in the boost::filesystem directly because it would introduce dependency on boost::regex.

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

    alternatively use FilteredRecursiveDirectoryIterator for recursive sub directories search:

    #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
    
    0 讨论(0)
  • 2020-11-27 04:11

    The accepted answer did not compile for me even when I used i->path().extension() instead of leaf(). What did work for me was an example from this website. Here's the code, modified, to apply a filter:

    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'm using Boost version: 1.53.0.

    Why we don't all just use glob() and some regex is beyond me.

    0 讨论(0)
  • 2020-11-27 04:18

    I believe the directory_iterators will only provide all files in a directory. It up to you to filter them as necessary.

    0 讨论(0)
  • 2020-11-27 04:28

    As mentioned at the end of Julien-L's post QDir is exactly what you want.
    https://qt-project.org/doc/qt-5.0/qtcore/qdir.html#QDir-3

    0 讨论(0)
  • 2020-11-27 04:31

    There is a Boost Range Adaptors way:

    #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 讨论(0)
  • 2020-11-27 04:34

    I was looking for a solution to this earlier and I think my solution is the simplest

    #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()));
                    }
    
                 //advance to first matching item if impl_ is not already at end()
                 if(impl_ != impl_type()) {
                      std::string s(impl_->path().string());
                      if( !boost::regex_match( s, rgx_ )) increment();
                 }
            }
            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_;
    };
    

    Then you can do

        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";});
    
    0 讨论(0)
提交回复
热议问题