【前言】使用C++进行指定目录下的文件夹/文件遍历不如pyhton等脚本语言便利,本文将博主日常使用的C++目录便利程序公布出来,并提供了一个小的例程讲解用法。该程序递归遍历给定目录下的所有子目录和文件,最终返回文件的绝对路径名。配合通配符filespec使用,可以返回保存有指定格式文件绝对路径string的vector容器。
1. CBrowseDir类
定义class CBrowseDir及其派生类CStatDir。CBrowseDir类的保护成员是m_saInitDir,存放初始目录的绝对路径,以'\'结尾。成员方法SetInitDir设置初始目录为dir,如果返回false,表示目录不可用。成员方法BeginBrowse和BeginBrowseFilenames,开始遍历初始目录及其子目录下由通配符filespec指定类型的文件。受保护成员方法BrowseDir和GetDirFilenames是最主要的函数,可以返回通配符指定类型文件的绝对路径。这两个函数的实现方法类似,都是递归地遍历指定目录下的子目录,从而遍历所有通配符指定的文件,只不过GetDirFilenames方法返回的是string类型的vector,包含通配符指定格式的文件的绝对路径,更加常用。
virtual bool ProcessFile是一个虚函数,在子类CStatDir中实现。函数BrowseDir每进入一个目录,就调用ProcessDir并把正在处理的目录名及上一级目录名作为参数传递过去。用户可以覆写ProcessFile函数,加入自己的处理代码,比如用户可以在这里统计子目录的个数。
#include "stdlib.h"
#include "direct.h"
#include "string.h"
#include "string"
#include "io.h"
#include "stdio.h"
#include <vector>
using namespace std;
class CBrowseDir
{
protected:
//存放初始目录的绝对路径,以'\'结尾
char m_szInitDir[_MAX_PATH];
public:
//缺省构造器
CBrowseDir();
//设置初始目录为dir,如果返回false,表示目录不可用
bool SetInitDir(const char *dir);
//开始遍历初始目录及其子目录下由filespec指定类型的文件
//filespec可以使用通配符 * ?,不能包含路径。
//如果返回false,表示遍历过程被用户中止
bool BeginBrowse(const char *filespec);
vector<string> BeginBrowseFilenames(const char *filespec);
protected:
//遍历目录dir下由filespec指定的文件
//对于子目录,采用迭代的方法
//如果返回false,表示中止遍历文件
bool BrowseDir(const char *dir, const char *filespec);
vector<string> GetDirFilenames(const char *dir, const char *filespec);
//函数BrowseDir每找到一个文件,就调用ProcessFile
//并把文件名作为参数传递过去
//如果返回false,表示中止遍历文件
//用户可以覆写该函数,加入自己的处理代码
virtual bool ProcessFile(const char *filename);
//函数BrowseDir每进入一个目录,就调用ProcessDir
//并把正在处理的目录名及上一级目录名作为参数传递过去
//如果正在处理的是初始目录,则parentdir=NULL
//用户可以覆写该函数,加入自己的处理代码
//比如用户可以在这里统计子目录的个数
virtual void ProcessDir(const char *currentdir, const char *parentdir);
};
//从CBrowseDir派生出的子类,用来统计目录中的文件及子目录个数
class CStatDir : public CBrowseDir
{
protected:
int m_nFileCount; //保存文件个数
int m_nSubdirCount; //保存子目录个数
public:
//缺省构造器
CStatDir()
{
//初始化数据成员m_nFileCount和m_nSubdirCount
m_nFileCount = m_nSubdirCount = 0;
}
//返回文件个数
int GetFileCount()
{
return m_nFileCount;
}
//返回子目录个数
int GetSubdirCount()
{
//因为进入初始目录时,也会调用函数ProcessDir,
//所以减1后才是真正的子目录个数。
return m_nSubdirCount - 1;
}
protected:
//覆写虚函数ProcessFile,每调用一次,文件个数加1
virtual bool ProcessFile(const char *filename)
{
m_nFileCount++;
return CBrowseDir::ProcessFile(filename);
}
//覆写虚函数ProcessDir,每调用一次,子目录个数加1
virtual void ProcessDir
(const char *currentdir, const char *parentdir)
{
m_nSubdirCount++;
CBrowseDir::ProcessDir(currentdir, parentdir);
}
};
2. BrowseDir.cpp
#include "BrowseDir.h"
#include "OpenCV_D.h"
#include <iostream>
#include <fstream>
#include "stdio.h"
using namespace cv;
using namespace std;
CBrowseDir::CBrowseDir()
{
//用当前目录初始化m_szInitDir
_getcwd(m_szInitDir, _MAX_PATH);
//如果目录的最后一个字母不是'\',则在最后加上一个'\'
int len = strlen(m_szInitDir);
if (m_szInitDir[len - 1] != '\\')
strcat_s(m_szInitDir, "\\");
}
bool CBrowseDir::SetInitDir(const char *dir)
{
//先把dir转换为绝对路径
if (_fullpath(m_szInitDir, dir, _MAX_PATH) == NULL)
return false;
//判断目录是否存在
if (_chdir(m_szInitDir) != 0)
return false;
//如果目录的最后一个字母不是'\',则在最后加上一个'\'
int len = strlen(m_szInitDir);
if (m_szInitDir[len - 1] != '\\')
strcat_s(m_szInitDir, "\\");
return true;
}
vector<string> CBrowseDir::BeginBrowseFilenames(const char *filespec)
{
ProcessDir(m_szInitDir, NULL);
return GetDirFilenames(m_szInitDir, filespec);
}
bool CBrowseDir::BeginBrowse(const char *filespec)
{
ProcessDir(m_szInitDir, NULL);
return BrowseDir(m_szInitDir, filespec);
}
bool CBrowseDir::BrowseDir(const char *dir, const char *filespec)
{
_chdir(dir);
//首先查找dir中符合要求的文件
long hFile;
_finddata_t fileinfo;
if ((hFile = _findfirst(filespec, &fileinfo)) != -1)
{
do
{
//检查是不是目录
//如果不是,则进行处理
if (!(fileinfo.attrib & _A_SUBDIR))
{
char filename[_MAX_PATH];
strcpy_s(filename, dir);
strcat_s(filename, fileinfo.name);
cout << filename << endl;
if (!ProcessFile(filename))
return false;
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
//查找dir中的子目录
//因为在处理dir中的文件时,派生类的ProcessFile有可能改变了
//当前目录,因此还要重新设置当前目录为dir。
//执行过_findfirst后,可能系统记录下了相关信息,因此改变目录
//对_findnext没有影响。
_chdir(dir);
if ((hFile = _findfirst("*.*", &fileinfo)) != -1)
{
do
{
//检查是不是目录
//如果是,再检查是不是 . 或 ..
//如果不是,进行迭代
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp
(fileinfo.name, "..") != 0)
{
char subdir[_MAX_PATH];
strcpy_s(subdir, dir);
strcat_s(subdir, fileinfo.name);
strcat_s(subdir, "\\");
ProcessDir(subdir, dir);
if (!BrowseDir(subdir, filespec))
return false;
}
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
return true;
}
vector<string> CBrowseDir::GetDirFilenames(const char *dir, const char *filespec)
{
_chdir(dir);
vector<string>filename_vector;
filename_vector.clear();
//首先查找dir中符合要求的文件
long hFile;
_finddata_t fileinfo;
if ((hFile = _findfirst(filespec, &fileinfo)) != -1)
{
do
{
//检查是不是目录
//如果不是,则进行处理
if (!(fileinfo.attrib & _A_SUBDIR))
{
char filename[_MAX_PATH];
strcpy_s(filename, dir);
strcat_s(filename, fileinfo.name);
filename_vector.push_back(filename);
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
//查找dir中的子目录
//因为在处理dir中的文件时,派生类的ProcessFile有可能改变了
//当前目录,因此还要重新设置当前目录为dir。
//执行过_findfirst后,可能系统记录下了相关信息,因此改变目录
//对_findnext没有影响。
_chdir(dir);
if ((hFile = _findfirst("*.*", &fileinfo)) != -1)
{
do
{
//检查是不是目录
//如果是,再检查是不是 . 或 ..
//如果不是,进行迭代
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp
(fileinfo.name, "..") != 0)
{
char subdir[_MAX_PATH];
strcpy_s(subdir, dir);
strcat_s(subdir, fileinfo.name);
strcat_s(subdir, "\\");
ProcessDir(subdir, dir);
vector<string>tmp = GetDirFilenames(subdir, filespec);
for (vector<string>::iterator it = tmp.begin(); it < tmp.end(); it++)
{
filename_vector.push_back(*it);
}
}
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
return filename_vector;
}
bool CBrowseDir::ProcessFile(const char *filename)
{
return true;
}
void CBrowseDir::ProcessDir(const char
*currentdir, const char *parentdir)
{
}
3. 进一步封装:Utils类
#include "BrowseDir.h"
#include <vector>
using namespace std;
class Utils
{
public:
// 缺省构造函数
Utils(){};
// 获取文件夹中所有指定的文件名(包括通配符)
vector<string> getFileList( char *dir, char *filespec = "*.*"); // 默认读取所有文件
};
#include "utils.h"
#include <iostream>
#include <fstream>
#include "stdio.h"
using namespace std;
vector<string> Utils::getFileList(char *dir, char *filespec){
CStatDir dirPath;
// Initial dir path
if (!dirPath.SetInitDir(dir)){
cout << "Source dir does not extst!" << endl;
exit(NULL); // exit the program
}
vector<string>vecFileList = dirPath.BeginBrowseFilenames(filespec); // 根据通配符获取文件名,存到fileList列表
return vecFileList;
}
4. 使用举例
#include "utils.h"
#include<iostream>
using namespace std;
int const BUF_LEN = 128;
int main(){
char srcDir[BUF_LEN] = "F:\\DataSet\\airDataSet";
Utils _util;
vector<string> vecFileName = _util.getFileList(srcDir, "*.jpg");
cout << "file Number: " << vecFileName.size() << endl;
return 0;
}