如何在Win32中递归创建文件夹?

时间:2009-10-07 10:28:20

标签: c++ winapi directory

我正在尝试创建一个带有目录名称(C:\foo\bar..\foo\bar\..\baz\\someserver\foo\bar)的函数,并根据需要创建目录以便整个路径已创建。

我正在尝试一种非常天真的实现,这似乎是一个字符串处理的噩梦。有/ vs \,网络共享的特殊情况以\\开头(你也不能尝试mkdir()前两个级别的路径机器名称和共享名称),并且路径中可以存在\.\类型的废话。

在C ++中是否存在一种简单的方法?

16 个答案:

答案 0 :(得分:45)

如果您不需要支持Windows 2000之前的Windows版本,则可以使用SHCreateDirectoryEx function。考虑一下:

int createDirectoryRecursively( LPCTSTR path )
{
    return SHCreateDirectoryEx( NULL, path, NULL );
}

// ...
if ( createDirectoryRecursively( T("C:\\Foo\\Bar\\Baz") ) == ERROR_SUCCESS ) {
   // Bingo!
} 

如果使用这样的shell32.dll API成为一个问题,你总是可以用其他东西重新实现上面的createDirectoryRecursively函数(可能是一个手工循环)。

答案 1 :(得分:10)

这是一个没有外部库的版本,因此只支持Win32,并且在所有版本的Windows(包括Windows CE,我需要它)中使用该功能:

wchar_t *path = GetYourPathFromWherever();

wchar_t folder[MAX_PATH];
wchar_t *end;
ZeroMemory(folder, MAX_PATH * sizeof(wchar_t));

end = wcschr(path, L'\\');

while(end != NULL)
{
    wcsncpy(folder, path, end - path + 1);
    if(!CreateDirectory(folder, NULL))
    {
        DWORD err = GetLastError();

        if(err != ERROR_ALREADY_EXISTS)
        {
            // do whatever handling you'd like
        }
    }
    end = wcschr(++end, L'\\');
}

答案 2 :(得分:7)

这是我写的一个迭代创建文件夹树的函数。这是主要功能:

#include <io.h>
#include <string>
#include <direct.h>
#include <list>

// Returns false on success, true on error
bool createFolder(std::string folderName) {
    list<std::string> folderLevels;
    char* c_str = (char*)folderName.c_str();

    // Point to end of the string
    char* strPtr = &c_str[strlen(c_str) - 1];

    // Create a list of the folders which do not currently exist
    do {
        if (folderExists(c_str)) {
            break;
        }
        // Break off the last folder name, store in folderLevels list
        do {
            strPtr--;
        } while ((*strPtr != '\\') && (*strPtr != '/') && (strPtr >= c_str));
        folderLevels.push_front(string(strPtr + 1));
        strPtr[1] = 0;
    } while (strPtr >= c_str);

    if (_chdir(c_str)) {
        return true;
    }

    // Create the folders iteratively
    for (list<std::string>::iterator it = folderLevels.begin(); it != folderLevels.end(); it++) {
        if (CreateDirectory(it->c_str(), NULL) == 0) {
            return true;
        }
        _chdir(it->c_str());
    }

    return false;
}

folderExists例程如下:

// Return true if the folder exists, false otherwise
bool folderExists(const char* folderName) {
    if (_access(folderName, 0) == -1) {
        //File not found
        return false;
    }

    DWORD attr = GetFileAttributes((LPCSTR)folderName);
    if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
        // File is not a directory
        return false;
    }

    return true;
}

我测试上述函数的示例调用如下(并且有效):

createFolder("C:\\a\\b\\c\\d\\e\\f\\g\\h\\i\\j\\k\\l\\m\\n\\o\\p\\q\\r\\s\\t\\u\\v\\w\\x\\y\\z");

此功能尚未经过非常彻底的测试,我不确定它是否适用于其他操作系统(但可能与一些修改兼容)。我目前正在使用Visual Studio 2010Windows 7.

答案 3 :(得分:6)

SHCreateDirectory功能可以做到这一点。但该文档声明它可以在Windows的更高版本中弃用。

来自MSDN

  

注意此功能是   可通过Windows XP Service获得   Pack 2(SP2)和Microsoft Windows   Server 2003.它可能被更改或   在后续版本中不可用   视窗。

答案 4 :(得分:3)

我的方便简单的工作版本:

BOOL DirectoryExists(LPCTSTR szPath)
{
  DWORD dwAttrib = GetFileAttributes(szPath);

  return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
    (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

void createDirectoryRecursively(std::wstring path)
{
  unsigned int pos = 0;
  do
  {
    pos = path.find_first_of(L"\\/", pos + 1);
    CreateDirectory(path.substr(0, pos).c_str(), NULL);
  } while (pos != std::string::npos);
}

//in application
int main()
{
  std::wstring directory = L"../temp/dir";
  if (DirectoryExists(directory.c_str()) == FALSE)
    createDirectoryRecursively(directory);
  return 0;
}

答案 5 :(得分:2)

适用于Windows XP及更高版本。将widechar null终止的字符串和递归操作的数量视为参数。尚未测试超过1级。

注意:路径分隔符必须为'\'

bool CreateRecursiveDirectoryW(const wchar_t* filepath, const int max_level)
{
    bool result = false;
    wchar_t path_copy[MAX_PATH] = {0};
    wcscat_s(path_copy, MAX_PATH, filepath);
    std::vector<std::wstring> path_collection;

    for(int level=0; PathRemoveFileSpecW(path_copy) && level < max_level; level++)
    {
        path_collection.push_back(path_copy);
    }
    for(int i=path_collection.size()-1; i >= 0; i--)
    {
        if(!PathIsDirectoryW(path_collection[i].c_str()))
            if(CreateDirectoryW(path_collection[i].c_str(), NULL))
                result = true;
    }
    return result;
};

答案 6 :(得分:1)

ctacke你忘记了最后一段。例如'\ AA \ BB \ “CC”' 以下是对ctacke的修改:

//---------------------------------------------------------------------
int isfexist(char *fn)
{
    struct stat stbuf;
    extern int errno;

    if (stat(fn, &stbuf)) {
        if (errno == ENOENT) return(0);
        else {
            printf("isfexist: stat");
            return(0);
        }
    } else {
        if (stbuf.st_mode & S_IFDIR) return(2);
        else return(1);
    }
}
//---------------------------------------------------------------------
int MakeDirTree(char *path)
{
    char *end1, *end2;

    if (path[0] == '\\') end1 = path + 1;       // Case '\aa\bb'
    else if (path[1] == ':' && path[2] == '\\') end1 = path + 3;    // Case 'C:\\aa\\bb'
    else end1 = path;

    for(;;) {
        end2 = strchr(end1, '\\');
        if (end2 == NULL) {
            // Case '\aa\bb\'
            if (*end1 == 0) break;
            // Last segment '\aa\bb\"cc"' not yet proceed
        } else *end2 = 0;
        if (isfexist(path) <= 0) mkdir(path);
        if (end2 == NULL) break;    // Last segment finished
        else {
            *end2 = '\\';
            end1 = end2 + 1;
        }
    }
}

答案 7 :(得分:1)

我正在修改一个旧的Windows CE应用程序,这正是我打算使用的。也应该在Windows CE中工作。这实际上也是递归的:

static void createPath(const CString& p)
{
   // only create directories that don't exist
   if (::GetFileAttributes(p) == INVALID_FILE_ATTRIBUTES)
   {
      // check if our parent needs to be created, too...
      int i = p.ReverseFind('\\');
      if (i > 0)
      {
         // ...yes, create the parent (recursively)
         createPath(p.Left(i));
      }

      // finally, actually create the directory in p
      ::CreateDirectory(p, NULL);
   }
}

答案 8 :(得分:1)

使用C ++ 17,可以使用std::filesystem::create_directories()轻松完成此操作。

示例:

#include <filesystem>
...

const char* path = "C:\\foo\\bar";
std::filesystem::create_directories(path);

答案 9 :(得分:0)

UnicodeString path = "C:\\Test\\Test\\Test\\";
TStringList *list = new TStringList();

try
{
    list->Delimiter = '\\';
    list->StrictDelimiter = true;
    list->DelimitedText = path;
    path = list->Strings[0]; \\drive letter
    for(int i = 1; i < list->Count - 1; i++)
    {
        try
        {
            path += "\\" + list->Strings[i];
            CreateDirectory(path.w_str(), NULL);
        }
        catch(...) { }
    }
}
catch(...) { }
delete list;

答案 10 :(得分:0)

下面的方法帮助我创建了几个目录,直到实现了整个路径。

假设您有:

import './App.css'; import 'antd/dist/antd.css'; import { ThemeProvider } from 'styled-components'; import theme from './configurations/theme'; import Routes from './configurations/routes' import { BrowserRouter, Switch } from "react-router-dom"; import AppStyle from "./pages/style"; import Header from './pages/Header'; import { UserContext } from './Context/Auth'; import { useContext, Suspense } from 'react'; import ExSideBar from './pages/ExSideBar'; import Footer from './pages/Footer'; import { ConfigProvider } from 'antd'; import PageLoading from './UI/PageLoading/PageLoading'; function App() { const { siteWidth, exSideBar, } = useContext(UserContext); return ( <Suspense fallback={<PageLoading />}> <ConfigProvider > <ThemeProvider theme={theme}> <BrowserRouter> <AppStyle siteWidth={siteWidth} > <Switch> <div className="siteContainer"> {exSideBar ? <div className="exSideBar"> <ExSideBar /> </div> : null} <div className="bodyContainer"> <div className="mainContent"> <Header /> <div className="body"> <div className="mainContainer"> <Routes /> </div> </div> </div> </div> </div> </Switch> <footer className="footer"> <Footer /> {/* <center> <Button onClick={() => changeLanguage('en')}>EN</Button> <Button onClick={() => changeLanguage('de')}>DE</Button> <Button onClick={() => changeLanguage('tr')}>TR</Button> </center> */} </footer> </AppStyle> </BrowserRouter> </ThemeProvider> </ConfigProvider> </Suspense> ); } export default App; 并且您将 C:\d1 作为参数传递。

这个函数会继续在 C:\d1\d2\d3 内创建 d2 并在 d1 内创建 d3。此外,它不会弄乱您的 d2 目录中已经存在的文件。

d1

答案 11 :(得分:0)

如果您使用的是 Microsoft 的 Windows Implementation Libraries (WIL),则可以使用 the wil::CreateDirectoryDeep function。如果没有,您可能需要考虑使用它,或借用代码。

答案 12 :(得分:-1)

来自http://www.cplusplus.com/reference/string/string/find_last_of/

// string::find_last_of
#include <iostream>
#include <string>
using namespace std;

void SplitFilename (const string& str)
{
  size_t found;
  cout << "Splitting: " << str << endl;
  found=str.find_last_of("/\\");
  cout << " folder: " << str.substr(0,found) << endl;
  cout << " file: " << str.substr(found+1) << endl;
}

int main ()
{
  string str1 ("/usr/bin/man");
  string str2 ("c:\\windows\\winhelp.exe");

  SplitFilename (str1);
  SplitFilename (str2);

  return 0;

这应该让你知道如何处理路径字符串。然后,您需要做的就是遍历从驱动器到最深文件夹的路径。检查文件夹是否存在,如果不存在,则创建它。

答案 13 :(得分:-1)

这是我的代码示例(从How can I create directory tree in C++/Linux?复制而来)。也许它不符合第一篇文章的所有要求,但很好,它适用于Windows和Linux:

#include <iostream>
#include <string>
#include <sys/stat.h> // stat
#include <errno.h>    // errno, ENOENT, EEXIST
#if defined(_WIN32)
#include <direct.h>   // _mkdir
#endif

bool isDirExist(const std::string& path)
{
#if defined(_WIN32)
    struct _stat info;
    if (_stat(path.c_str(), &info) != 0)
    {
        return false;
    }
    return (info.st_mode & _S_IFDIR) != 0;
#else 
    struct stat info;
    if (stat(path.c_str(), &info) != 0)
    {
        return false;
    }
    return (info.st_mode & S_IFDIR) != 0;
#endif
}

bool makePath(const std::string& path)
{
#if defined(_WIN32)
    int ret = _mkdir(path.c_str());
#else
    mode_t mode = 0755;
    int ret = mkdir(path.c_str(), mode);
#endif
    if (ret == 0)
        return true;

    switch (errno)
    {
    case ENOENT:
        // parent didn't exist, try to create it
        {
            int pos = path.find_last_of('/');
            if (pos == std::string::npos)
#if defined(_WIN32)
                pos = path.find_last_of('\\');
            if (pos == std::string::npos)
#endif
                return false;
            if (!makePath( path.substr(0, pos) ))
                return false;
        }
        // now, try to create again
#if defined(_WIN32)
        return 0 == _mkdir(path.c_str());
#else 
        return 0 == mkdir(path.c_str(), mode);
#endif

    case EEXIST:
        // done!
        return isDirExist(path);

    default:
        return false;
    }
}

int main(int argc, char* ARGV[])
{
    for (int i=1; i<argc; i++)
    {
        std::cout << "creating " << ARGV[i] << " ... " << (makePath(ARGV[i]) ? "OK" : "failed") << std::endl;
    }
    return 0;
}

用法:

d:\Work\c++\make_path> makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK

答案 14 :(得分:-1)

void createFolders(const std::string &s, char delim) {
    std::stringstream ss(s);
    std::string item;
    char combinedName[50]={'\0'};
    while (std::getline(ss, item, delim)) { 
        sprintf(combinedName,"%s%s%c",combinedName,item.c_str(),delim);
        cout<<combinedName<<endl;

        struct stat st = {0};
                 if (stat(combinedName,&st)==-1)
                 { 
            #if REDHAT
                     mkdir(combinedName,0777);
            #else
                      CreateDirectory(combinedName,NULL);
            #endif
                 }


     }
}

答案 15 :(得分:-9)

注意:这个答案有点快,很脏,并不能处理所有情况。如果你没问题,请继续阅读。如果没有,请考虑使用其他选项之一。


你可以使用好的旧mkdir。跑吧

system("mkdir " + strPath);

你已经完成了。

好吧,差不多。您仍然需要处理一些案例,例如网络共享(可能不起作用)和反斜杠。但是当使用相对安全的路径时,您可以使用这种较短的形式。

你可能会觉得有用的另一件事就是摆脱可能的滋扰是_fullpath(),它会将给定的路径解析为一个完整而干净的路径。知道你有一个干净的路径,你应该没有问题写一个相当简单的递归函数,即使在处理UNC路径时也会逐个创建文件夹。