搜索文件的iterator 目前要改进的地方是每个目录要search两次,原因是windows下没有有类似unix的fnmatch函数,如果有的话只用一次了.已经测试过的平台(公司有的平台)
1.windows2000(其它没测试)
2.redhat linux 9.0
3.IBM AIX 4.3.3
4.HP UNIX 11i v2(内部版本 11.23)
如果大家有其它的环境(各种不同的linux,sco unix,suse linux),也可以进行一下测试
findfile.cpp(测试用的cpp文件) 代码: #include "FindFileContainer.h"
#include <vector>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <functional>
using namespace std;
struct CompareFileName : public unary_function<file_info,bool>
{
CompareFileName(const string& name):name_(name){}
bool operator()(const file_info& lrs) const
{
if (lrs.name() > name_)
return true;
return false;
}
private:
string name_;
};
ostream& operator<<(ostream& os,const file_info& fileinfo)
{
if (fileinfo.issubdirectory())
{
os << "D\t";
}
else
{
os << "F\t";
}
os << fileinfo.dir() << fileinfo.name();
return os;
}
// STL not have copy_if function,just have remove_copy_if, we can
// use remove_copy_if to implement copy_if
template<typename InputIterator, typename OutputIterator, typename Pred>
OutputIterator copy_if(InputIterator first,InputIterator last,OutputIterator result,Pred pred)
{
return remove_copy_if(first,last,result,not1(pred));
}
//findfile path pattern case-i
int main(int argc,char* argv[])
{
if ( argc != 4)
{
printf("usage:findfile path pattern case-i\n");
return 1;
}
bool ci = (argv[3][0] == '1');
FindFileContainer findfile(argv[1],argv[2],both,true,ci);
FindFileContainer::iterator begin = findfile.begin();
FindFileContainer::iterator end = findfile.end();
copy_if(begin,end,ostream_iterator<file_info>(cout,"\n"),CompareFileName("A"));
return 0;
} FindFileContainer.h (文件容器的头文件) 代码: /*
Name: FindFileContainer.h
Author: papercrane(Heheng Li) ,spitfire(HuanMing Zheng)
Description: Iterate files recursively
Date: 2003.06.11
Copyright: 2003 papercrane 2004 spitfire,papercrane
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation. No representations are made about the
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
Version: 1.1
Last update: 2004.05.26
*/
#ifndef FINDFILECONTAINER_H_
#define FINDFILECONTAINER_H_
#include <string>
#include "FindFileWrapper.h"
class FindFileContainer
{
public:
class iterator;
FindFileContainer(
const std::string& basepath = "", // base path to start iteration
const std::string& object = "*.*", // object file name pattern
searchmode mode = fileonly, // one of searchmode
bool recurse = false, // need to find in subdirectory?
bool insensitively = false, // case-insensitively ,in windows,this agrument not use
char delim = pathdelim);
~FindFileContainer();
iterator begin();
iterator end();
class iterator
{
public:
friend class FindFileContainer;
friend class file_info;
typedef file_info value_type;
typedef int difference_type;
typedef file_info* pointer;
typedef file_info& reference;
typedef const file_info* const_pointer;
typedef const file_info& const_reference;
typedef std::input_iterator_tag iterator_category;
iterator();
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& other);
bool operator!=(const iterator& other);
const_reference operator*();
const_pointer operator->();
private:
explicit iterator(FindFileContainer* self);
explicit iterator(int);
enum status {s_normal,s_end,s_null};
FindFileContainer* container_;
status status_;
int count_;
};
private:
friend class iterator;
FindFileWrapper find_;
FindFileContainer(const FindFileContainer&);
FindFileContainer& operator=(const FindFileContainer&);
};
#endif // defined(FINDFILECONTAINER_H_) FindFileContainer.cpp 代码:
// FindFileContainer.cpp
/*
Name: FindFileContainer.cpp
Author: papercrane(Heheng Li) ,spitfire(HuanMing Zheng)
Description: Implementation for FindFileContainer.h
Date: 2003.06.11
Copyleft: 2003 papercrane 2004 spitfire,papercrane
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation. No representations are made about the
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
Version: 1.1
Last update: 2004.05.26
*/
#include "FindFileContainer.h"
using std::string;
FindFileContainer::FindFileContainer(const string& basepath, const string& object,
searchmode mode, bool recurse,bool insensitively, char delim)
: find_(basepath,object,mode,recurse,insensitively,delim)
{
}
FindFileContainer::~FindFileContainer(void)
{
}
FindFileContainer::iterator FindFileContainer::begin()
{
iterator p(this);
return p;
}
FindFileContainer::iterator FindFileContainer::end()
{
iterator p(1);
return p;
}
//the default ctor
FindFileContainer::iterator::iterator():container_(NULL),status_(s_null),count_(-1)
{
}
//this ctor create a end iterator,it's private ctor
FindFileContainer::iterator::iterator(int):container_(NULL),status_(s_end),count_(-1)
{
}
//this ctor create a begin iterator,it't private ctor
FindFileContainer::iterator::iterator(FindFileContainer* self):container_(self),status_(s_null),count_(0)
{
status_ = s_normal;
container_->find_.start_find();
if (container_->find_.is_end())
{
status_ = s_end;
}
else
{
status_ = s_normal;
count_ = 1;
}
}
bool FindFileContainer::iterator::operator==(const FindFileContainer::iterator& other)
{
if(status_ == s_end && status_ == other.status_) return true;
return false;
}
bool FindFileContainer::iterator::operator!=(const FindFileContainer::iterator& other)
{
return !operator==(other);
}
FindFileContainer::iterator& FindFileContainer::iterator::operator++()
{
if (status_ == s_null)
{
throw FindIteratorError("use a NULL FindFileContainer::iterator");
}
container_->find_.find_next();
if (container_->find_.is_end())
{
status_ = s_end;
}
else
{
status_ = s_normal;
++count_;
}
return *this;
}
FindFileContainer::iterator FindFileContainer::iterator::operator++(int)
{
iterator tmp = *this;
operator++();
return tmp;
}
const file_info& FindFileContainer::iterator::operator*()
{
return container_->find_.getfileinfo();
}
const file_info* FindFileContainer::iterator::operator->()
{
return &container_->find_.getfileinfo();
} FindFileWrapper.h (隔离不同平台的find实现) 代码: /*
Name: FindFileWrapper.h
Author: papercrane(Heheng Li) ,spitfire(HuanMing Zheng)
Description: Iterate files recursively
Date: 2003.06.11
Copyleft: 2003 papercrane 2004 spitfire,papercrane
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation. No representations are made about the
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
Version: 1.1
Last update: 2004.05.31
*/
#ifndef FINDFILEWRAPPER_H_
#define FINDFILEWRAPPER_H_
enum searchmode { fileonly, dironly, both };
#ifdef _WIN32
#ifdef _MSC_VER
#pragma warning(disable:4786)
#endif
#include <io.h>
typedef unsigned int attrib_t;
const char pathdelim = '\\';
struct finddata
{
long handle;
_finddata_t find_t;
};
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
const char pathdelim = '/';
struct finddata
{
DIR* dir;
struct dirent* dirent_t;
struct stat statbuf;
};
typedef mode_t attrib_t;
#endif
#include <list>
#include <string>
#include <stdexcept>
class FindIteratorError : public std::runtime_error
{
public:
FindIteratorError(const std::string& s):runtime_error(s){}
};
class file_info
{
public:
file_info()
:issubdirectory_(false),
attrib_(0),time_create_(0),
time_access_(0),
time_write_(0),
size_(0),name_(""),
dir_("")
{
}
bool issubdirectory(void) const
{
return issubdirectory_;
}
attrib_t attrib(void) const
{
return attrib_;
}
time_t createtime(void) const
{
return time_create_;
}
time_t accesstime(void) const
{
return time_access_;
}
time_t writetime(void) const
{
return time_write_;
}
size_t size(void) const
{
return size_;
}
const std::string& name() const
{
return name_;
}
const std::string& dir() const
{
return dir_;
}
private:
friend class FindFileWrapper;
bool issubdirectory_;
attrib_t attrib_;
time_t time_create_;
time_t time_access_;
time_t time_write_;
size_t size_;
std::string name_;
std::string dir_;
};
class FindFileWrapper
{
public:
FindFileWrapper(
const std::string& basepath, // base path to start iteration
const std::string& object, // object file name pattern
searchmode mode , // one of searchmode
bool recurse, // need to find in subdirectory?
bool insensitively, // case-insensitively ,in windows,this agrument not use
char delim );
~FindFileWrapper();
void start_find(void);
bool is_end(void) const;
void find_next(void);
const file_info& getfileinfo();
private:
void dir(std::string& s) const;
// reset the state for iteration
void reset(void);
// put all subdirs into the stack
void pushsubdirs(const std::string& path);
// pop up a subdir from the stack
void popsubdir(std::string& path);
// search in subdirs for files
void findinsubdir();
// find a file in the path given
bool findonefile(bool start);
// set full name of current path
void setcurpath();
// is file found proper to return
bool suitableforsearchmode(void) const;
// no more files in current path
bool nomorefile(void) const;
// no more subdirs in the stack
bool nomoresubdir(void) const;
std::string basepath_; // base path
std::string object_; // object file name pattern, wildcard permitted
int searchmode_; // one of searchmode
bool recurse_; // need to find in subdirectory?
const char delim_; // char to seperate the path
bool insensitively_;
finddata finddata_; // file find data for C CRT API
file_info file_info_;
std::string cursubdir_; // subdir full path iterated currently
typedef std::list<std::string> dirstack;
dirstack subdirs_; // stack storing subdirs(full name) to be visited
};
#endif // defined(FINDFILEWRAPPER_H_) FindFileWrapper.cpp 代码: /*
Name: FindFileWraper.cpp
Author: papercrane(Heheng Li) ,spitfire(HuanMing Zheng)
Description: Implementation for FindFileWrapper.h
Date: 2003.06.11
Copyleft: 2003 papercrane 2004 spitfire,papercrane
Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation. No representations are made about the
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
Version: 1.1
Last update: 2004.05.31
*/
#include "FindFileWrapper.h"
#include <cassert>
#include <cstring>
using std::string;
using std::list;
inline void normalizepath(string& path, char delim)
{
// make sure it's delim-end except null path
// \ or / differs from null as base path,
// where \ or / indicates root directory
// and null indicates current directory
int len = path.length();
if(len > 0 && path[len - 1] != delim)
path.append(1,delim);
}
#ifdef _WIN32
namespace
{
const long invalid_search_handle = -1L;
const int end_search = -1;
const char* any_mask = "*.*";
const bool START = true;
const bool CONTINUE = false;
void init_finddata(finddata& data)
{
memset(&data,0,sizeof(data));
data.handle = invalid_search_handle;
}
void my_find_close(finddata& data)
{
if (data.handle != invalid_search_handle)
{
_findclose(data.handle);
data.handle = invalid_search_handle;
memset(&data.find_t,0,sizeof(data.find_t));
}
}
bool my_find_first(const string& path,const string& pattern,finddata& data,bool insensitively )
{
string find = path + pattern;
data.handle = _findfirst(find.c_str(),&data.find_t);
if (data.handle == invalid_search_handle)
{
return false;
}
return true;
}
bool my_find_next(const string& path,const string& pattern,finddata& data,bool insensitively )
{
assert(data.handle != invalid_search_handle);
if (_findnext(data.handle,&data.find_t) == end_search)
{
my_find_close(data);
return false;
}
return true;
}
inline
bool my_is_close(const finddata& data)
{
return data.handle == invalid_search_handle;
}
inline
bool my_is_subdir(const finddata& data)
{
return (data.find_t.attrib & _A_SUBDIR) != 0;
}
inline
attrib_t my_attrib(const finddata& data)
{
return data.find_t.attrib;
}
inline
time_t my_time_create(const finddata& data)
{
return data.find_t.time_create;
}
inline
time_t my_time_access(const finddata& data)
{
return data.find_t.time_access;
}
inline
time_t my_time_write(const finddata& data)
{
return data.find_t.time_write;
}
inline
int my_size(const finddata& data)
{
return data.find_t.size;
}
inline
const char* my_name(const finddata& data)
{
return data.find_t.name;
}
inline
bool my_isspecialfile(const finddata& data)
{
// pay attention, if argument 3 is replaced by 1 in first call,
// data.name as ".*" will generate 0 as result, which may cause
// defect, while if replaced by 2 in second call, "..*" may also
// make trouble here
return strncmp(data.find_t.name,".",2) == 0
|| strncmp(data.find_t.name,"..",3) == 0;
}
}
#else //if not windows os
#include <fnmatch.h>
#include <limits.h>
#include <algorithm>
#include <unistd.h>
namespace
{
//if not support S_ISLNK marco,define it
//if not support link file (no define S_IFLNK),define S_ISLNK return false;
#ifdef S_IFLNK
#ifndef S_ISLNK
#define S_ISLNK(mode) (((mode)& S_IFMT) == S_IFLNK)
#endif //end ifndef S_ISLNK
#else //not define S_IFLNK
#define S_ISLNK(mode) (1==0)
#endif //end ifdef S_IFLNK
//if os not support S_ISDIR marco,define it
#ifndef S_ISDIR
#define S_ISDIR(mode) (((mode)& S_IFMT) == S_IFDIR)
#endif
const char* any_mask = "*";
const bool START = true;
const bool CONTINUE = false;
const int PATH_MAX_GUESS = 2048;
int get_path_max() //
{
int pathmax = 0;
if((pathmax=pathconf("/",_PC_PATH_MAX)) < 0)
{
pathmax = PATH_MAX_GUESS;
}
else
{
++pathmax;
}
return pathmax;
}
class PathAlloc
{
public:
PathAlloc():block_(new char[get_path_max()])
{
}
~PathAlloc()
{
if (block_)
{
delete []block_;
}
}
char* getblock() const {return block_;}
private:
char* block_;
};
bool matchfile(const char* pattern,const char* filename,bool insensitively)
{
#ifdef FNM_CASEFOLD //FNM_CASEFOLD flag is GNU extensions.
int flag = 0;
if (insensitively) flag = FNM_CASEFOLD;
return fnmatch(pattern,filename,flag);
#else
if (insensitively) //case-insensitively
{
static PathAlloc patternbuffer;
static PathAlloc filenamebuffer;
static int pathmax = get_path_max();
unsigned int patternsize = strlen(pattern);
unsigned int filenamesize = strlen(filename);
assert(patternsize <= pathmax);
assert(filenamesize <= pathmax);
std::transform(pattern,pattern+patternsize,patternbuffer.getblock(),toupper);
std::transform(filename,filename+filenamesize,filenamebuffer.getblock(),toupper);
patternbuffer.getblock()[patternsize] = '\0';
filenamebuffer.getblock()[filenamesize] = '\0';
return fnmatch(patternbuffer.getblock(),filenamebuffer.getblock(),0);
}
else
{
return fnmatch(pattern,filename,0);
}
#endif
}
void init_finddata(finddata& data)
{
memset(&data,0,sizeof(data));
}
void my_find_close(finddata& data)
{
if (data.dir)
{
closedir(data.dir);
data.dir = NULL;
}
}
bool my_find_first(const string& path,const string& pattern,finddata& data,bool insensitively)
{
data.dir = opendir(path.c_str());
if (NULL == data.dir)
{
return false;
}
while((data.dirent_t = readdir(data.dir)) != NULL)
{
if (matchfile(pattern.c_str(),data.dirent_t->d_name,insensitively) == 0)
{
string fullname = path + data.dirent_t->d_name;
lstat(fullname.c_str(),&data.statbuf);
return true;
}
}
my_find_close(data);
return false;
}
bool my_find_next(const string& path,const string& pattern,finddata& data,bool insensitively)
{
assert(data.dir != NULL);
while((data.dirent_t = readdir(data.dir)) != NULL)
{
if (matchfile(pattern.c_str(),data.dirent_t->d_name,insensitively) == 0)
{
string fullname = path + data.dirent_t->d_name;
lstat(fullname.c_str(),&data.statbuf);
return true;
}
}
my_find_close(data);
return false;
}
bool my_is_close(const finddata& data)
{
return data.dir == NULL;
}
bool my_is_subdir(const finddata& data)
{
return S_ISDIR(data.statbuf.st_mode) && !S_ISLNK(data.statbuf.st_mode);
}
attrib_t my_attrib(const finddata& data)
{
return data.statbuf.st_mode;
}
time_t my_time_create(const finddata& data)
{
return data.statbuf.st_ctime;
}
time_t my_time_access(const finddata& data)
{
return data.statbuf.st_atime;
}
time_t my_time_write(const finddata& data)
{
return data.statbuf.st_mtime;
}
int my_size(const finddata& data)
{
return data.statbuf.st_size;
}
const char* my_name(const finddata& data)
{
return data.dirent_t->d_name;
}
bool my_isspecialfile(const finddata& data)
{
// pay attention, if argument 3 is replaced by 1 in first call,
// data.name as ".*" will generate 0 as result, which may cause
// defect, while if replaced by 2 in second call, "..*" may also
// make trouble here
return strncmp(data.dirent_t->d_name,".",2) == 0
|| strncmp(data.dirent_t->d_name,"..",3) == 0;
}
}
#endif
FindFileWrapper::FindFileWrapper(const string& basepath, const string& object,
searchmode mode, bool recurse,bool insensitively, char delim)
: basepath_(basepath), object_(object), searchmode_(mode),
recurse_(recurse), delim_(delim),insensitively_(insensitively)
{
// append delim to base path if necessary
normalizepath(basepath_,delim_);
init_finddata(finddata_);
}
FindFileWrapper::~FindFileWrapper(void)
{
// shut down the file find handle
my_find_close(finddata_);
}
void FindFileWrapper::dir(string& s) const
{
s = cursubdir_;
}
void FindFileWrapper::start_find(void)
{
// shut down last opened file find handle
reset();
cursubdir_ = basepath_;
// initialize depth-first searching
if(recurse_) pushsubdirs(basepath_);
// find one available file in base path
if(findonefile(START)) return;
// try in subdirectories
if(recurse_) findinsubdir();
}
bool FindFileWrapper::is_end(void) const
{
return nomorefile() && nomoresubdir();
}
void FindFileWrapper::find_next(void)
{
// new subdirectory currently visited will be retrieved
// durring findinsubdir() in begin() or operator++()
// in recurse iteration, or durring findonefile(const string&)
// in begin() in both iterations
// find one available file in current path
if(findonefile(CONTINUE)) return;
// if not found, try in subdirectories
if(recurse_) findinsubdir();
}
void FindFileWrapper::reset(void)
{
// shut down file find handle
my_find_close(finddata_);
subdirs_.clear();
}
void FindFileWrapper::pushsubdirs(const string& path)
{
finddata data;
// quit if no files or subdirectories at all
if(!my_find_first(path,any_mask,data,insensitively_)) return;
dirstack newsubdirs;
do
{
// some os tells . and .. is subdirectory,
// but static method issubdir shows they aren't,
// for safety, check anyway
if(my_is_subdir(data) && !my_isspecialfile(data))
newsubdirs.push_back(path + my_name(data) + delim_);
}
while(my_find_next(path,any_mask,data,insensitively_));
// push as stack, so if ascending order is perferred,
// it should be sorted in descending order
// newsubdirs.sort(std::greater<string>());
// push into real stack
subdirs_.splice(subdirs_.end(),newsubdirs);
}
inline
void FindFileWrapper::popsubdir(string& path)
{
// peek and pop up
path = subdirs_.back();
subdirs_.pop_back();
}
void FindFileWrapper::findinsubdir(void)
{
// depth-first searching
while(!subdirs_.empty())
{
popsubdir(cursubdir_);
pushsubdirs(cursubdir_);
// search current subdirectory for files
if(findonefile(START)) break;
}
}
bool FindFileWrapper::findonefile(bool start)
{
if(start)
{
// no files or subdirectories matched at all
if(!my_find_first(cursubdir_,object_,finddata_,insensitively_)) return false;
while(true)
{
// some os tells . and .. is subdirectory,
if(!my_isspecialfile(finddata_) && suitableforsearchmode())
return true;
if(!my_find_next(cursubdir_,object_,finddata_,insensitively_))
{
return false;
}
}
}
bool findover = true;
// find next file until available found or searching ended
while(my_find_next(cursubdir_,object_,finddata_,insensitively_))
{
if(suitableforsearchmode())
{
findover = false;
break;
}
}
if(findover)
{
my_find_close(finddata_);
}
return !findover;
}
bool FindFileWrapper::suitableforsearchmode(void) const
{
switch(searchmode_)
{
case fileonly: return !my_is_subdir(finddata_);
case dironly: return my_is_subdir(finddata_);
case both: return true;
default: assert(false); return false;
}
}
inline
bool FindFileWrapper::nomorefile(void) const
{
return my_is_close(finddata_);
}
inline
bool FindFileWrapper::nomoresubdir(void) const
{
return !recurse_ || subdirs_.empty();
}
const file_info& FindFileWrapper::getfileinfo()
{
file_info_.issubdirectory_ = my_is_subdir(finddata_);
file_info_.attrib_ = my_attrib(finddata_);
file_info_.time_create_ = my_time_create(finddata_);
file_info_.time_access_ = my_time_access(finddata_);
file_info_.time_write_ = my_time_write(finddata_);
file_info_.size_ = my_size(finddata_);
file_info_.name_ = my_name(finddata_);
dir(file_info_.dir_);
return file_info_;
} |