Introduction
Several times I worked with file names, I usually used Win32 API such as ::FindFirstFile
.. But it turns out that it's so boring work. Finally, I realized I can use STL's great feature, iterator, to handle file name iteration. That's why I made a simple STL iterator class for file name iteration.
Usage
win32_file_iterator itBegin("c:\\*.*"), itEnd;
std::copy(itBegin, itEnd, ostream_iterator<std::string>(cout, "\n"));
The code above shows the simplest way to use the class. Actually, you can use almost all of STL algorithm, I think..
win32_file_iterator itBegin("c:\\*.*"), itEnd;
std::vector<std::string> vec(itBegin, itEnd);
You also can fill the STL container by using the constructor that takes begin iterator and end iterator.
Actually, win32_file_iterator
class' constructor takes three parameters. The first one is the filter string that is for calling ::FindFirstFile
function. Second one is the flag that specifies whether dereferenced path is full path or not. For example, if it's true, the returned path string is c:\test\aa.txt, otherwise it'll be aa.txt only. The last parameter is the other flags which specify file attribute. For simplicity, I used Win32 API's FILE_ATTRIBUTE_XXX
flags..
If you want to get only directory names, and which is full path, the code will look like this:
win32_file_iterator itBegin("c:\\*", true, FILE_ATTRIBUTE_DIRECTORY);
So easy, huh?
Source
#include <windows.h>
#include <iterator>
#include <string>
class win32_file_iterator :
public std::iterator<std::input_iterator_tag, std::string>
{
private:
class internal_handle_data{
public:
internal_handle_data():_h(NULL), _ref(0){}
void setHandle(HANDLE handle){ _h = handle; }
HANDLE getHandle(){ return _h; }
void incRef(){ _ref++; }
unsigned decRef(){ return --_ref; }
operator HANDLE(){ return _h; }
private:
HANDLE _h;
unsigned _ref;
};
public:
win32_file_iterator(std::string strfilter, bool bFullPath = false,
int flag = FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY):
_bEnd(false), _bFullPath(bFullPath), _flag(flag){
HANDLE h = ::FindFirstFile(strfilter.c_str(), &_wfd);
_handle.setHandle(h);
if(h == INVALID_HANDLE_VALUE){
_bEnd = true;
}else{
_handle.incRef();
std::string::size_type n1 = strfilter.find_last_of("\\");
_strroot = strfilter.substr(0,n1+1);
_chkvalid(_wfd);
}
}
win32_file_iterator():_bEnd(true){}
win32_file_iterator(win32_file_iterator& rhs){
_handle = rhs._handle;
_handle.incRef();
_flag = rhs._flag;
_bFullPath = rhs._bFullPath;
_bEnd = rhs._bEnd;
_wfd = rhs._wfd;
_strfname = rhs._strfname;
_strroot = rhs._strroot;
}
~win32_file_iterator(){
if(_handle.decRef() == 0 && _handle.getHandle() != NULL ){
FindClose(_handle);
}
}
reference operator*(){
return _strfname;
}
bool operator==(const win32_file_iterator& rhs) const{
return (_bEnd == rhs._bEnd);
}
bool operator!=(const win32_file_iterator& rhs) const{
return (_bEnd != rhs._bEnd);
}
win32_file_iterator& operator++(){
_findnext();
return *this;
}
win32_file_iterator& operator++(int){
_findnext();
return *this;
}
private:
void _findnext(){
BOOL b = ::FindNextFile(_handle, &_wfd);
if(b){
_chkvalid(_wfd);
}else{
_bEnd = true;
}
}
void _chkvalid(WIN32_FIND_DATA& _wfd){
if(_wfd.dwFileAttributes & _flag){
_getval(_wfd);
}
else{
_findnext();
}
}
void _getval(WIN32_FIND_DATA& wfd){
if(_bFullPath)
_strfname = _strroot+ wfd.cFileName;
else
_strfname = wfd.cFileName;
}
private:
int _flag;
bool _bFullPath;
bool _bEnd;
internal_handle_data _handle;
WIN32_FIND_DATA _wfd;
std::string _strroot;
std::string _strfname;
};
Comment
The code might have many terrible bugs. But what I want was to show the way we can use STL like iteration to find filenames. I wish it'll help you. You can use this code in whatever ways you want, comments are welcome..
And also check out boost::filesystem
library.. it's well-written but a little bit heavy. It needs an additional DLL, I suppose.