日常工程中经常用到遍历文件夹内所有文件或指定格式文件。
因为主要都是做图像方面,OpenCV库接触较多,其中有实现遍历文件夹下所有文件的类Directory。
Directory里面包括3个成员函数:
(1)、GetListFiles:遍历指定文件夹下的所有文件,不包括指定文件夹内的文件夹;
(2)、GetListFolders:遍历指定文件夹下的所有文件夹,不包括指定文件夹下的文件;
(3)、GetListFilesR:R的含义,我的理解应该是recursive,即递归地遍历指定文件夹下的所有文件,遇到文件夹内的文件夹则进入文件夹继续遍历子文件夹。
在opencv2.x版本中,该类实现在contrib模块中,要使用Directory类,则需包含contrib.hpp头文件。
用过一段时间opencv3.x,这个版本采用主核+插件框架,contrib库并没有包含进来,需要自己重新编译contrib。
其中声明如下:
class CV_EXPORTS Directory
{
public:
static std::vector<std::string> GetListFiles ( const std::string& path, const std::string & exten = "*", bool addPath = true );
static std::vector<std::string> GetListFilesR ( const std::string& path, const std::string & exten = "*", bool addPath = true );
static std::vector<std::string> GetListFolders( const std::string& path, const std::string & exten = "*", bool addPath = true );
};
其中三个参数分别是文件夹路径、所需遍历文件格式以及是否添加路径的标志位,true则返回带路径的完整文件名。
当我调用GetListFiles、GetListFolders时能够正确得到结果。
如vector<string> fileNameWithPath = dir.GetListFiles(FilePath, FileExten, true);
则能够得到文件夹下所有指定格式文件名, 同理GetListFolders时能得到文件夹名称。测试代码简单,此处省略。
!!可是当我调用GetListFilesR时出现问题!!:
vector<string> fileNameWithOutPath = dir.GetListFilesR(FilePath, FileExten, false);
本来目的是提取出文件夹包括其所有子文件夹中所有指定格式文件并储存,但实际上却出现了问题,只能够提取当前目录,并没有递归访问子文件夹中文件。
查看GetListFilesR源码后发现问题:
std::vector<std::string> Directory::GetListFilesR ( const std::string& path, const std::string & exten, bool addPath )
{
std::vector<std::string> list = Directory::GetListFiles(path, exten, addPath);
std::vector<std::string> dirs = Directory::GetListFolders(path, exten, addPath);
std::vector<std::string>::const_iterator it;
for (it = dirs.begin(); it != dirs.end(); ++it)
{
std::vector<std::string> cl = Directory::GetListFiles(*it, exten, addPath);
list.insert(list.end(), cl.begin(), cl.end());
}
return list;
}
根据源码分析,如果我想提取所有txt格式文件,则exten参数传入“*.txt”,可是源码中将提取文件夹的函数GetListFolders中也传入了同样的exten参数,这样显然是不能提取出文件夹的,所以只能提取出一级文件夹下txt格式文件。
如果将GetListFilesR函数中
std::vector<std::string> dirs = Directory::GetListFolders(path, exten, addPath);
中传入的参数exten改为“*”,则运行时dirs不会总是为空,一级文件夹中有子文件夹时,能够提取出二级文件夹名称并保存。
但是如此依然存在问题,GetListFilesR函数中调用GetListFolders传入参数exten会影响提取文件夹文件名,而传入的参数addPath则会影响访问二级文件夹。
如果GetListFilesR函数需要返回路径,即addPath==true时,不会出现问题,能够正常访问。但是如果只想存储文件名,不包含路径,传入的addPath==false时,在调用
std::vector<std::string> dirs = Directory::GetListFolders(path, “*”, addPath);
后,dirs中保存的,只是二级文件夹的短名称,并没有加上路径,则下面for循环中对二级文件夹的访问并不能成功执行,所以应该改为
std::vector<std::string> dirs = Directory::GetListFolders(path, “*”, true);
即在递归遍历时,访问的总应该是不带格式的文件夹,且存储其路径以便后续访问。
接下来的for循环,函数访问二级文件夹中文件。
虽然如上所述已经改了部分,但是这样改依旧不能达到预期目标。for循环中,调用的是GetListFiles函数,也就是说,它并没有实现recursive,能够提取出二级文件夹并保存,接下来函数访问二级文件夹中文件,但是也仅限于二级文件夹中文件,如果二级文件夹中存在三级文件夹,且三级文件夹中有我们需要的格式的文件(如txt),仅仅循环调用GetListFiles是不能实现递归访问的。应该调用自身的GetListFilesR完成递归。
总的来说,即将GetListFilesR源码改为如下:
std::vector<std::string> Directory::GetListFilesR(const std::string& path, const std::string & exten, bool addPath)
{
std::vector<std::string> list = Directory::GetListFiles(path, exten, addPath);
std::vector<std::string> dirs = Directory::GetListFolders(path, "*", true);
std::vector<std::string>::const_iterator it;
for (it = dirs.begin(); it != dirs.end(); ++it)
{
std::vector<std::string> cl = Directory::GetListFilesR(*it, exten, addPath);
list.insert(list.end(), cl.begin(), cl.end());
}
return list;
}
此时调用
vector<string> fileNameWithOutPath = dir.GetListFilesR(FilePath, FileExten, false);
能够正确返回指定目录下所有指定格式的文件。
附opencv中Directory类源码:
#include "opencv2/contrib/contrib.hpp"
#include <cvconfig.h>
#if defined(WIN32) || defined(_WIN32)
#include <windows.h>
#include <tchar.h>
#else
#include <dirent.h>
#endif
namespace cv
{
std::vector<std::string> Directory::GetListFiles( const std::string& path, const std::string & exten, bool addPath )
{
std::vector<std::string> list;
list.clear();
std::string path_f = path + "/" + exten;
#ifdef WIN32
#ifdef HAVE_WINRT
WIN32_FIND_DATAW FindFileData;
#else
WIN32_FIND_DATAA FindFileData;
#endif
HANDLE hFind;
#ifdef HAVE_WINRT
wchar_t wpath[MAX_PATH];
size_t copied = mbstowcs(wpath, path_f.c_str(), MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
hFind = FindFirstFileExW(wpath, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);
#else
hFind = FindFirstFileA((LPCSTR)path_f.c_str(), &FindFileData);
#endif
if (hFind == INVALID_HANDLE_VALUE)
{
return list;
}
else
{
do
{
if (FindFileData.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_HIDDEN ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_SYSTEM ||
FindFileData.dwFileAttributes == FILE_ATTRIBUTE_READONLY)
{
char* fname;
#ifdef HAVE_WINRT
char fname_tmp[MAX_PATH] = {0};
size_t copied = wcstombs(fname_tmp, FindFileData.cFileName, MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
fname = fname_tmp;
#else
fname = FindFileData.cFileName;
#endif
if (addPath)
list.push_back(path + "/" + std::string(fname));
else
list.push_back(std::string(fname));
}
}
#ifdef HAVE_WINRT
while(FindNextFileW(hFind, &FindFileData));
#else
while(FindNextFileA(hFind, &FindFileData));
#endif
FindClose(hFind);
}
#else
(void)addPath;
DIR *dp;
struct dirent *dirp;
if((dp = opendir(path.c_str())) == NULL)
{
return list;
}
while ((dirp = readdir(dp)) != NULL)
{
if (dirp->d_type == DT_REG)
{
if (exten.compare("*") == 0)
list.push_back(static_cast<std::string>(dirp->d_name));
else
if (std::string(dirp->d_name).find(exten) != std::string::npos)
list.push_back(static_cast<std::string>(dirp->d_name));
}
}
closedir(dp);
#endif
return list;
}
std::vector<std::string> Directory::GetListFolders( const std::string& path, const std::string & exten, bool addPath )
{
std::vector<std::string> list;
std::string path_f = path + "/" + exten;
list.clear();
#ifdef WIN32
#ifdef HAVE_WINRT
WIN32_FIND_DATAW FindFileData;
#else
WIN32_FIND_DATAA FindFileData;
#endif
HANDLE hFind;
#ifdef HAVE_WINRT
wchar_t wpath [MAX_PATH];
size_t copied = mbstowcs(wpath, path_f.c_str(), path_f.size());
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
hFind = FindFirstFileExW(wpath, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0);
#else
hFind = FindFirstFileA((LPCSTR)path_f.c_str(), &FindFileData);
#endif
if (hFind == INVALID_HANDLE_VALUE)
{
return list;
}
else
{
do
{
#ifdef HAVE_WINRT
if (FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY &&
wcscmp(FindFileData.cFileName, L".") != 0 &&
wcscmp(FindFileData.cFileName, L"..") != 0)
#else
if (FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY &&
strcmp(FindFileData.cFileName, ".") != 0 &&
strcmp(FindFileData.cFileName, "..") != 0)
#endif
{
char* fname;
#ifdef HAVE_WINRT
char fname_tmp[MAX_PATH];
size_t copied = wcstombs(fname_tmp, FindFileData.cFileName, MAX_PATH);
CV_Assert((copied != MAX_PATH) && (copied != (size_t)-1));
fname = fname_tmp;
#else
fname = FindFileData.cFileName;
#endif
if (addPath)
list.push_back(path + "/" + std::string(fname));
else
list.push_back(std::string(fname));
}
}
#ifdef HAVE_WINRT
while(FindNextFileW(hFind, &FindFileData));
#else
while(FindNextFileA(hFind, &FindFileData));
#endif
FindClose(hFind);
}
#else
(void)addPath;
DIR *dp;
struct dirent *dirp;
if((dp = opendir(path_f.c_str())) == NULL)
{
return list;
}
while ((dirp = readdir(dp)) != NULL)
{
if (dirp->d_type == DT_DIR &&
strcmp(dirp->d_name, ".") != 0 &&
strcmp(dirp->d_name, "..") != 0 )
{
if (exten.compare("*") == 0)
list.push_back(static_cast<std::string>(dirp->d_name));
else
if (std::string(dirp->d_name).find(exten) != std::string::npos)
list.push_back(static_cast<std::string>(dirp->d_name));
}
}
closedir(dp);
#endif
return list;
}
std::vector<std::string> Directory::GetListFilesR ( const std::string& path, const std::string & exten, bool addPath )
{
std::vector<std::string> list = Directory::GetListFiles(path, exten, addPath);
std::vector<std::string> dirs = Directory::GetListFolders(path, exten, addPath);
std::vector<std::string>::const_iterator it;
for (it = dirs.begin(); it != dirs.end(); ++it)
{
std::vector<std::string> cl = Directory::GetListFiles(*it, exten, addPath);
list.insert(list.end(), cl.begin(), cl.end());
}
return list;
}
}