2009-10-07 47 views
30

我试图创建一个函数,它需要一个目录的名称(C:\foo\bar..\foo\bar\..\baz\\someserver\foo\bar),并根据需要创建目录以便创建整个路径。如何在Win32中递归创建文件夹?

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

在C++中是否存在一种简单的方法来做到这一点?

+1

为什么不使用Win32 API for那东西? – 2009-10-07 10:32:02

+0

@JonathanFeinberg:在版本8之前的Windows中,路径名的解析受到严重限制(MAX_PATH长度限制,不支持“\\?\”前缀等)。您的评论确实适用于Windows 8及更高版本。 – IInspectable 2016-02-02 11:50:26

回答

-9

注:这个答案是有点快和肮脏,并且不处理的所有情况。如果这对你有用,请继续阅读。如果不是,请考虑使用其他选项之一。


您可以使用良好的旧mkdir。刚刚运行

system("mkdir " + strPath); 

你就完成了。

好吧,差不多。还有一些情况需要注意,例如网络共享(可能无法使用)和反斜杠。但是当使用相对安全的路径时,可以使用这种较短的形式。

另一件你可能会发现有用的东西摆脱可能的滋扰是_fullpath(),这将解决给定的路径到一个完整和干净的。知道你有一条干净的道路,即使在处理UNC路径时,你也应该没有问题写一个相当简单的递归函数来逐个创建文件夹。

+1

我并不自豪,但这是我们的解决方案。非常短,适用于网络共享..太容易忽略了。 – pauldoo 2009-10-08 19:15:40

+0

(也不依赖于shell32 ..) – pauldoo 2009-10-08 19:16:21

+0

好吧,如果它让你感觉好一点,所有的路径格式化和递归创建都已经在mkdir中实现了。所以原则上,这就像使用boost,winstl或任何其他第三方解决方案... – eran 2009-10-08 19:56:33

5

SHCreateDirectory功能可以做到这一点。但该文件指出,它可能会在Windows的更高版本中被弃用。

从MSDN

注意此功能是通过了Windows XP Service 包2(SP2)和Microsoft Windows中 的Windows的后续版本 Server 2003中它可能被更改或 不可用 可用。

40

如果您不需要支持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功能。

+0

警告词SHCreateDirectoryEx在处理隐藏目录时有特殊的行为,并可能向用户显示用户界面对话框。 – ShadowChaser 2012-03-08 21:16:49

+0

如果传递hwnd引用,它只显示用户界面对话框,而不是NULL。 – Joakim 2012-12-13 13:07:57

+0

@Joakim:即使HWND为NULL,你也可能会因为它可以处理非排队的消息而出现令人讨厌的惊喜。 – EFraim 2013-03-03 14:53: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; 

这应该给你如何应对路径字符串的想法。之后,您只需循环从驱动器开始到最深文件夹的路径。检查文件夹是否存在,如果不存在,请创建它。

5

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

#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.

+1

这个功能乍看起来很不错,但它将当前目录更改为新创建的目录,这是您在使用相对路径时可能不希望发生的情况。 – antipattern 2015-01-13 14:18:32

+0

不使用统一码是一种罪魁祸首,并且一直贯穿于这个千年。不知道为什么你将第一个参数强制转换成与GetFileAttributes类似的类型, – IInspectable 2016-02-02 11:32:42

9

这里是一个没有外部库工作的版本,所以仅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'\\'); 
} 
+0

看起来不错,但如果我正确读取它,它需要路径有一个尾随“\”,否则像“C:\文件夹\子文件夹”路径不会创建“子文件夹”,但“C:\文件夹\子文件夹\“将。循环完成后,您可能需要额外调用CreateDirectory(路径)。 – 2016-05-27 22:22:14

+1

这是为了获取完全合格的文件路径,它将确保目标文件夹存在。如果你只想传递一个文件夹路径,那么是的,它需要进一步的工作 – ctacke 2016-05-28 17:26:41

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; 
     } 
    } 
} 
+1

很没用,没有Unicode支持。 – IInspectable 2016-02-02 11:36:38

2

对于Windows XP及以上。期望以widechar空字符结束的字符串和递归操作的数量作为参数。没有超过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; 
}; 
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); 
    } 
} 
+0

我实际上有机会在Win32和WinCE上测试这个,似乎工作正常。 – Tommi 2014-11-13 15:09:22

+0

适合我。 – 2016-02-22 10:11:03

-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 
+0

没有Unicode支持,这绝对不适用于Windows。 – IInspectable 2016-02-02 11:41:07

-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 
       } 


    } 
} 
+0

你可以添加一些文字来解释你的答案吗? – Kmeixner 2016-04-14 13:50:00

+0

createFloders(“a/b/c/d/e”,'/'); – 2016-04-15 08:46:54

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; 
3

方便和简单的工作版本mine:

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; 
}