如何递归地删除一个目录?

8
5个回答

14
自C++17起,最好的解决方案是使用std::filesystem::remove_all。在此之前,您可以使用Boost实现的boost::filesystem::remove_all。无论哪种方式,您都不必担心平台特定的问题。 我不知道任何其他独立于平台的解决方案;否则通常的方法将涉及读取目录并递归下降它(但读取目录的方式也使用std::filesystemboost::filesystem或依赖于系统的代码)。

5
如果您准备使用Windows API,最简单的方法是调用SHFileOperation实现此目的。使用FO_DELETE操作,并记得双重空终止您的目录名称。

1
请注意,SHFileOperation非常适用于用户模式应用程序,但在从Windows服务调用时不起作用,特别是如果正在使用模拟身份来删除UNC网络共享上的目录。对于这种情况,您需要做繁重的工作,并使用FindFirstFile()/FindNext()和DeleteFile()/RemoveDirectory()方法。 - WebDrive

3
根据 MSDN 的介绍,当使用相对路径时,SHFileOperation 不是线程安全的。它只能与绝对路径一起安全使用。
我建议改用以下代码:
double directory_delete(char *pathname)
{
    string str(pathname);
    if (!str.empty())
    {
        while (*str.rbegin() == '\\' || *str.rbegin() == '/')
        {
            str.erase(str.size()-1);
        }
    }
    replace(str.begin(),str.end(),'/','\\');

    struct stat sb;
    if (stat((char *)str.c_str(),&sb) == 0 &&
        S_ISDIR(sb.st_mode))
    {
            HANDLE hFind;
            WIN32_FIND_DATA FindFileData;

            TCHAR DirPath[MAX_PATH];
            TCHAR FileName[MAX_PATH];

            _tcscpy(DirPath,(char *)str.c_str());
            _tcscat(DirPath,"\\*");
            _tcscpy(FileName,(char *)str.c_str());
            _tcscat(FileName,"\\");

            hFind = FindFirstFile(DirPath,&FindFileData);
            if (hFind == INVALID_HANDLE_VALUE) return 0;
            _tcscpy(DirPath,FileName);

            bool bSearch = true;
            while (bSearch)
            {
                if (FindNextFile(hFind,&FindFileData))
                {
                    if (!(_tcscmp(FindFileData.cFileName,".") &&
                        _tcscmp(FindFileData.cFileName,".."))) continue;
                    _tcscat(FileName,FindFileData.cFileName);
                    if ((FindFileData.dwFileAttributes &
                    FILE_ATTRIBUTE_DIRECTORY))
                    {
                        if (!directory_delete(FileName))
                        {
                            FindClose(hFind);
                            return 0;
                        }
                        RemoveDirectory(FileName);
                        _tcscpy(FileName,DirPath);
                    }
                    else
                    {
                        if (FindFileData.dwFileAttributes &
                            FILE_ATTRIBUTE_READONLY)
                            _chmod(FileName, _S_IWRITE);

                        if (!DeleteFile(FileName))
                        {
                            FindClose(hFind);
                            return 0;
                        }
                        _tcscpy(FileName,DirPath);
                    }
                }
                else
                {
                    if (GetLastError() == ERROR_NO_MORE_FILES)
                        bSearch = false;
                    else
                    {
                        FindClose(hFind);
                        return 0;
                    }
                }
            }
            FindClose(hFind);

            return (double)(RemoveDirectory((char *)str.c_str()) == true);
    }
    else
    {
        return 0;
    }
}

如果您想直接使用我的代码,需要在您的cpp文件顶部添加以下标头:

#include <windows.h> // winapi
#include <sys/stat.h> // stat
#include <tchar.h> // _tcscpy,_tcscat,_tcscmp
#include <string> // string
#include <algorithm> // replace

using namespace std;

...我觉得就是这样。

我的代码基于这篇文章:

http://www.codeguru.com/cpp/w-p/files/folderdirectorymaintenance/article.php/c8999/删除目录包括子文件夹.htm

我强烈建议不要使用SHFileOperation,除了安全问题外,它已经被IFileOperation取代自Windows Vista。


3
通常情况下,如果没有库函数可用,则会通过递归来完成此操作。一个函数迭代所有目录条目,删除“普通”文件,并使用找到的任何目录路径调用自身。 这将销毁整个目录树(我的Windows版本对传递的路径进行显式检查,以防止在意外传递自杀参数的情况下破坏OS文件夹)。

2
这可能有点老套,但考虑使用:
system("rd /s /q ...");

虽然它看起来不太好看,但它太简单了,不能忽视。它还解决了所有关于网络共享文件处理的问题。无论你想出什么解决方案,都可能是rd的(不完整和/或不正确的)重新实现,因此调用外部进程实际上是很好的代码重用。 ;-)


代码重用和不可移植性,如果rd不存在甚至没有编译错误。对于非常小的程序,这可能是有用且正确的,但对于长期运行的大型程序来说则不然。个人而言,我总是会检查该命令是否存在。 - Sebastian Mach
@phresnel:这个问题仅适用于Windows系统,并且“rd”在所有Windows系统上都是默认可用的。我敢说,“rd”可用的可能性比SHFileOperation可用的可能性更大(SHFileOperation自Windows XP以来一直存在,并且已经在Windows Vista中被弃用)。 - Frerich Raabe
1
概率对我来说不太相关。就我个人而言,如果某些功能不可用,我希望在编译时出现错误;但是使用系统时你永远不会有编译时错误。正如所说,对于小型、特定于系统的程序,这可能还可以接受。但是对于较大或长期存在的程序,我建议不要这样做。你永远不知道5、10、15年后会发生什么。如果突然所有人都转向Mac或Linux,那么肯定你也希望你的摇钱树能够在该平台上运行。 - Sebastian Mach
1
但是,如果我让代码在编译时失败,那么回归问题被忽略的概率为0%,而使用system()则在1%到100%之间。我总是努力开发可以运行或无法运行的代码。半个运行的程序真的很难调试。如果rd真的节省了我的时间,那么我会把一部分节省下来的时间用于运行时验证。 - Sebastian Mach
“太简单以至于不能忽略” - 实际上,它非常简单以至于应该被忽略,就像一直被忽略一样。一个无法报告操作结果的实现是毫无用处的。system的返回值完全没有意义。 - IInspectable
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接