遍历目录并读取目录下的所有文件,这个功能经常用,也简单,很多年前就看过网上的代码,感觉写复杂了,而且还浪费栈,发文的人说会“爆栈”(而且还不是一个人)。当时看到那些代码就觉得写的不好,不过觉得无关痛痒没有发博文。
N年过去,遇到类似的情况,有点忍不住,这么简单的东西。今天专门去搜索了网上(摆渡和谷歌上面的代码,包括StackOverflow上面的代码,不禁说出周星星电影《破坏之王》里面断水流大师兄的那段话。
找到的代码,几乎都是类似的,代码长,真浪费内存,速度又慢,还没有错误判断。
找一个稍好的例子。
bool ListDirectoryContents(const wchar_t *sDir)
{
WIN32_FIND_DATA fdFile;
HANDLE hFind = NULL;
wchar_t sPath[2048];
//Specify a file mask. *.* = We want everything!
wsprintf(sPath, L"%s\\*.*", sDir);
if((hFind = FindFirstFile(sPath, &fdFile)) == INVALID_HANDLE_VALUE)
{
wprintf(L"Path not found: [%s]\n", sDir);
return false;
}
do
{
//Find first file will always return "."
// and ".." as the first two directories.
if(wcscmp(fdFile.cFileName, L".") != 0
&& wcscmp(fdFile.cFileName, L"..") != 0)
{
//Build up our file path using the passed in
// [sDir] and the file/foldername we just found:
wsprintf(sPath, L"%s\\%s", sDir, fdFile.cFileName);
//Is the entity a File or Folder?
if(fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
wprintf(L"Directory: %s\n", sPath);
ListDirectoryContents(sPath); //Recursion, I love it!
}
else{
wprintf(L"File: %s\n", sPath);
}
}
}
while(FindNextFile(hFind, &fdFile)); //Find the next file.
FindClose(hFind); //Always, Always, clean things up!
return true;
}
网上的都是类似的,还有说遍历复杂云云,其实不复杂,代码短并且省内存(这里主要是省栈),速度更快。
这样写的时候,想想就发现,有两个地方可以优化,第一,局部变量sPath,这里占用内存大,而且还每次都复制一次,速度慢;第二、局部变量fdFile,这个占用内存也大......
又大又慢,所以会爆栈。
我现在记事本写一个比这个好的(N年前就这么写了,还有牛人说不需要递归,当时想了一下,我也实现了)。
UINT ListFiles(const TCHAR *pathname, UINT pathnamelength, UINT pathnamesize, WIN32_FIND_DATA *pfd)
{
HANDLE hfind;
UINT l;
UINT result = 0;
if (pathnamelength > 0 && pathname[pathnamelength - 1] != _T('\\'))
{
pathname[pathnamelength++] = _T('\\');
}
pathname[pathnamelength+0] = _T('*');
pathname[pathnamelength+1] = _T('.');
pathname[pathnamelength+2] = _T('*');
pathname[pathnamelength+3] = _T('\0');
hfind =FindFirstFile(pathname, pfd);
if(hfind != INVALID_HANDLE_VALUE)
{
do
{
// 这里可以优化的,留给有追求的人优化,不过这里不是关键
if(_tcscmp(pfd->cFileName, _T(".")) != 0&& _tcscmp(pfd->cFileName, _T("..")) != 0)
{
l = _tcslen(pfd->cFileName);
// 避免越界
if (pathnamelength + l < pathnamesize)
{
// 重用了占用内存的pathname和pfd
_tcscpy(pathname + pathnamelength, pfd->cFileName);
// 已经获取了文件信息,爱干嘛干嘛去
if(pfd->dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
{
result +=ListFiles(pathname, pathnamelength + l, pathnamesize, pfd);
}
else{
result++;
}
}
}
}while(FindNextFile(hfind, pfd));
FindClose(hfind);
}
return (result);
}
我在网上看到linux下遍历的代码也大同小异,也没有优化,都可以类似这么写的。
不骄傲,一个小代码而已,N年前就会这么写了。