views:

169

answers:

3

Hi, How to implement the ls "filename_[0-5][3-4]?" like class? The result I would like to store in the vector.

Currently I am using system() which is calling ls, but this is not portable under MS.

thanks, Arman.

+2  A: 

You can use boost::filesystem which has a portable way to retrieve files in a directory.

Then you can check the files against a regular expression with boost::regex for instance to only keep the ones that match your pattern.

Nikko
Thanks, I am not familiar with regex, could you please give an example of parsing my pattern?
Arman
You can check the documentation from boost: http://www.boost.org/doc/libs/1_43_0/libs/regex/doc/html/boost_regex/syntax/perl_syntax.htmlas far as I recall, it will not be very different from "filename_[0-5][3-4]"
Nikko
This glob pattern happens to be a regular expression too, as a matter of fact. You could change `[3-4]` to `[34]` and maintain the same meaning, but apart from that it happens to stay the same.
wilhelmtell
+5  A: 

The following program lists files in the current directory whose name matches the regular expression filename_[0-5][34]:

#include <boost/filesystem.hpp>
#include <boost/regex.hpp>  // also functional,iostream,iterator,string
namespace bfs = boost::filesystem;

struct match : public std::unary_function<bfs::directory_entry,bool> {
    bool operator()(const bfs::directory_entry& d) const {
        const std::string pat("filename_[0-5][34]");
        std::string fn(d.filename());
        return boost::regex_match(fn.begin(), fn.end(), boost::regex(pat));
    }
};

int main(int argc, char* argv[])
{
    transform_if(bfs::directory_iterator("."), bfs::directory_iterator(),
                 std::ostream_iterator<std::string>(std::cout, "\n"),
                 match(),
                 mem_fun_ref(&bfs::directory_entry::filename));
    return 0;
}

I omitted the definition of transform_if() for brevity. It isn't a standard function but it should be straightforward to implement.

wilhelmtell
When you try and compile this, remember to add the missing `#include` s mentioned in the comment, define the missing `transform_if()` (I can give you a hand if need be), and link against the filesystem and regex libraries. With g++ you can link against these libraries by specifying `-lboost_filesystem -lboost_system -lboost_regex` in the commandline. You might not need the `-lboost_system` flag if you're on Linux.
wilhelmtell
This is incredible!!! Works on linux and MS. Thanks!BTW: the transform_if I use like this: template<class In,class Out,class Pred, class Op> Out transform_if(In first,In last,Out res,Pred p,Op op) { while(first!=last){ if (p(*first)) *res = op(*first); ++first;++res; } return res; }
Arman
@Arman Yep, that looks right!
wilhelmtell
A: 

The boost solution is portable, but not optimal on Windows. The reason is that it calls FindFirstFile("*.*") and thus returns everything. Given the globbing pattern, it would be more efficient to call FindFirstFile("filename_?*.*"). You'd still have to filter the results (using e.g. Boost::regex) but this would exclude many files that can't possibly match.

Also, using either method don't forget to filter out directories before doing the regex matching. The check whether an entry is a directory is quite cheap.

MSalters
Really? Even with `boost::filesystem::directory_iterator`? I can see that's not the case on OS X.
wilhelmtell
I'm probably misunderstanding your comment, but I can't see why OSX matters when I explain where Boost on Windows is not optimal.
MSalters
I didn't contradict you. I just said that's not the case on OS X. Chill.
wilhelmtell
@MSalters: Thanks for pointing the performance hits. Personally for me the performance on MS does not play a role. All production runs are running on linux.
Arman
@WilhelmTell : aha, now I understand. Basically the Win32 API accepts a filter as an input, but neither POSIX `readdir` nor `boost::filesystem::directory_iterator::directory_iterator` do. As a result, on Windows Boost has to use the "*.*" pattern which is implicit on POSIX.
MSalters