1. 程式人生 > >沒看到能打的,遍歷目錄並讀取目錄下的檔案列表。(C語言,SDK)

沒看到能打的,遍歷目錄並讀取目錄下的檔案列表。(C語言,SDK)

遍歷目錄並讀取目錄下的所有檔案,這個功能經常用,也簡單,很多年前就看過網上的程式碼,感覺寫複雜了,而且還浪費棧,發文的人說會“爆棧”(而且還不是一個人)。當時看到那些程式碼就覺得寫的不好,不過覺得無關痛癢沒有發博文。

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年前就會這麼寫了。