首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++ 遞歸刪除目錄樹:支援子目錄與檔案的完整實作

     編輯:VC 教學

前言

C++ 標準庫與 MFC 僅提供刪除空目錄的 RemoveDirectory/CFileFind::RemoveDirectory。實務上常需刪除包含檔案與多層子目錄的整棵目錄樹。本文提供基於 MFC CFileFind 的遞歸刪除函式 DeleteDirectory,完整處理檔案屬性、錯誤回報與資源釋放。

函式原型與介面


// 返回值:成功 TRUE,失敗 FALSE
// 參數:DirName - 絕對路徑,如 "C:\\Temp",不可含尾隨反斜線
BOOL DeleteDirectory(char* DirName);

完整實作代碼


BOOL DeleteDirectory(char* DirName)
{
    CFileFind tempFind;
    char tempFileFind[200];
    char foundFileName[200];
    char tempPath[200];

    // 1. 建立搜尋模式 "DirName\*.*"
    sprintf(tempFileFind, "%s\\*.*", DirName);

    // 2. 開始尋找第一個項目
    BOOL IsFinded = tempFind.FindFile(tempFileFind);
    while (IsFinded)
    {
        IsFinded = tempFind.FindNextFile();

        // 跳過 "." 與 ".."
        if (!tempFind.IsDots())
        {
            // 取得檔名(不含路徑)
            strcpy(foundFileName, tempFind.GetFileName().GetBuffer(200));

            if (tempFind.IsDirectory())
            {
                // 3. 遞歸刪除子目錄
                sprintf(tempPath, "%s\\%s", DirName, foundFileName);
                if (!DeleteDirectory(tempPath))
                    return FALSE; // 子目錄刪除失敗,向上傳播
            }
            else
            {
                // 4. 刪除檔案(先移除唯讀屬性)
                sprintf(tempPath, "%s\\%s", DirName, foundFileName);
                SetFileAttributes(tempPath, FILE_ATTRIBUTE_NORMAL);
                if (!DeleteFile(tempPath))
                    return FALSE;
            }
        }
    }
    tempFind.Close();

    // 5. 刪除當前目錄(此刻應已空)
    if (!RemoveDirectory(DirName))
    {
        MessageBox(NULL, "刪除目錄失敗!", "警告", MB_OK);
        return FALSE;
    }
    return TRUE;
}

關鍵步驟解析

  1. 搜尋模式:"DirName\\*.*" 匹配目錄下所有項目(含隱藏、系統檔)。
  2. IsDots():MFC 封裝,自動過濾 . (當前目錄) 與 .. (父目錄),避免無限遞歸。
  3. IsDirectory():區分目錄與檔案,決定遞歸或直接刪除。
  4. SetFileAttributes(..., FILE_ATTRIBUTE_NORMAL):關鍵步驟!移除唯讀、隱藏、系統屬性,否則 DeleteFile/RemoveDirectory 會失敗。
  5. 遞歸回傳值檢查:子目錄刪除失敗立即返回 FALSE,避免繼續執行無意義操作。
  6. 最後刪除空目錄:所有內容清空後,RemoveDirectory 才能成功。

改良版:錯誤碼回報與 Unicode 支援


#include 
#include 
#include 

BOOL DeleteDirectoryEx(LPCTSTR DirName, LPDWORD pErrCode = NULL)
{
    TCHAR searchPath[MAX_PATH];
    StringCchPrintf(searchPath, MAX_PATH, _T("%s\\*.*"), DirName);

    WIN32_FIND_DATA wfd;
    HANDLE hFind = FindFirstFile(searchPath, &wfd);
    if (hFind == INVALID_HANDLE_VALUE)
    {
        if (pErrCode) *pErrCode = GetLastError();
        return FALSE;
    }

    BOOL success = TRUE;
    do
    {
        if (wcscmp(wfd.cFileName, _T(".")) == 0 || wcscmp(wfd.cFileName, _T("..")) == 0)
            continue;

        TCHAR fullPath[MAX_PATH];
        StringCchPrintf(fullPath, MAX_PATH, _T("%s\\%s"), DirName, wfd.cFileName);

        if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            if (!DeleteDirectoryEx(fullPath, pErrCode))
            {
                success = FALSE;
                break;
            }
        }
        else
        {
            SetFileAttributes(fullPath, FILE_ATTRIBUTE_NORMAL);
            if (!DeleteFile(fullPath))
            {
                if (pErrCode) *pErrCode = GetLastError();
                success = FALSE;
                break;
            }
        }
    }
    while (FindNextFile(hFind, &wfd));
    FindClose(hFind);

    if (success)
    {
        SetFileAttributes(DirName, FILE_ATTRIBUTE_NORMAL);
        if (!RemoveDirectory(DirName))
        {
            if (pErrCode) *pErrCode = GetLastError();
            success = FALSE;
        }
    }
    return success;
}

常見失敗場景與對策

場景錯誤碼對策
檔案被其他程式鎖定ERROR_SHARING_VIOLATION (32)嘗試關閉鎖定程式、重啟後再刪、或用 MoveFileEx 延遲刪除
權限不足ERROR_ACCESS_DENIED (5)以管理員身分執行、調整 ACL、或啟用 SeTakeOwnershipPrivilege
路徑過長 (>260)ERROR_FILENAME_EXCED_RANGE (206)使用 \\\\?\\ 前綴 + Unicode API (FindFirstFileW 等)
目錄非空但 RemoveDirectory 失敗ERROR_DIR_NOT_EMPTY (145)檢查遞歸邏輯是否漏刪隱藏/系統檔
網路磁碟機延遲ERROR_BAD_NETPATH (53)重試機制、超時處理

現代 C++17 替代方案:std::filesystem


#include 
namespace fs = std::filesystem;

bool DeleteDirectoryModern(const std::string& path)
{
    try
    {
        fs::remove_all(path); // 一行搞定:遞歸刪除目錄樹
        return true;
    }
    catch (const fs::filesystem_error& e)
    {
        std::cerr << "Error: " << e.what() << " (code: " << e.code() << ")\n";
        return false;
    }
}

std::filesystem::remove_all 內部處理了屬性移除、符號連結、權限錯誤等細節,是現代 C++ 專案的首選。

無論使用 MFC、Win32 API 或 C++17 filesystem,核心邏輯皆為「深度優先遞歸:先清空內容,再刪除目錄本身」,掌握此模式即可應對各種目錄清理需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved