【问题标题】:Can I use a mask to iterate files in a directory with Boost?我可以使用掩码在 Boost 目录中迭代文件吗?
【发布时间】:2010-11-18 11:13:05
【问题描述】:

我想遍历与somefiles*.txt 类似的目录中的所有文件。

boost::filesystem 是否有内置功能来执行此操作,或者我是否需要针对每个 leaf() 使用正则表达式或其他东西?

【问题讨论】:

    标签: c++ boost filesystems


    【解决方案1】:

    编辑:如 cmets 中所述,以下代码适用于 v3 之前的 boost::filesystem 版本。对于 v3,请参考 cmets 中的建议。


    boost::filesystem没有通配符搜索,需要自己过滤文件。

    这是一个使用boost::filesystemdirectory_iterator 提取目录内容并使用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().string(), what, my_filter ) ) continue;
    
        // File matches, store it
        all_matching_files.push_back( i->leaf() );
    }
    

    (如果您正在寻找具有内置目录过滤功能的即用型类,请查看 Qt 的 QDir。)

    【讨论】:

    • 感谢您提供非常完整的答案。给其他人的两个注意事项:1)leaf在文件系统v3(当前默认)中被弃用,使用path().filename()代替2)如果过滤条件是扩展名(非常常见)使用i->path( ).extension() == ".txt" [例如] 比正则表达式
    • leaf() 现在已弃用。 i->leaf() 可以替换为 i->path().string() 或 i->path().filename().string() 如果你只想要文件名
    • 正则表达式中的反斜杠必须转义"somefiles.*\\.txt"
    • @Julien-L 我实际上不得不将过滤器更改为const boost::regex my_filter( tarrget_path + "somefiles.*\.txt" );
    【解决方案2】:

    有一个Boost Range Adaptors的方式:

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

    【讨论】:

    • 好一个!范围适配器来救援。
    • 对于现在遇到这种情况的人:std::regex_match 和 boost::regex_match 不再接受临时字符串。对于上面给出的示例,这意味着需要将 lambda 主体调整为类似 auto oFile=path,filename().string();返回 xxx::regex_match(oFile, ...)。
    【解决方案3】:

    我的解决方案与 Julien-L 基本相同,但封装在包含文件中,使用起来更方便。使用 boost::filesystem v3 实现。我想类似的东西没有直接包含在 boost::filesystem 中,因为它会引入对 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());
                }
            );
    

    或者使用 FilteredRecursiveDirectoryIterator 进行递归子目录搜索:

    #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】:

      我相信 directory_iterators 只会提供目录中的所有文件。您可以根据需要过滤它们。

      【讨论】:

        【解决方案5】:

        即使我使用i-&gt;path().extension() 而不是leaf(),接受的答案也没有为我编译。对我有用的是this website 的一个例子。这是应用过滤器的修改后的代码:

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

        我使用的是 Boost 版本:1.53.0。

        为什么我们不都只使用glob() 而一些正则表达式超出了我的范围。

        【讨论】:

          【解决方案6】:

          正如Julien-L 的帖子末尾提到的,QDir 正是您想要的。
          https://qt-project.org/doc/qt-5.0/qtcore/qdir.html#QDir-3

          【讨论】:

            【解决方案7】:

            我之前一直在寻找解决方案,我认为我的解决方案是最简单的

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

            那你就可以了

                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";});
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-11-29
              • 1970-01-01
              • 2017-12-19
              • 2018-01-12
              • 2017-01-04
              • 2015-07-27
              相关资源
              最近更新 更多