C++ 如何在 Win32 中递归创建文件夹?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1530760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I recursively create a folder in Win32?
提问by pauldoo
I'm trying to create a function that takes the name of a directory (C:\foo\bar
, or ..\foo\bar\..\baz
, or \\someserver\foo\bar
), and creates directories as necessary so that the whole path is created.
我正在尝试创建一个函数,该函数采用目录名称(C:\foo\bar
, or ..\foo\bar\..\baz
, or \\someserver\foo\bar
),并根据需要创建目录,以便创建整个路径。
I am attempting a pretty naive implementation of this myself and it seems to be a string processing nightmare. There is /
vs \
, there is the special case of network shares which begin with \\
(also you can't attempt to mkdir() the first two levels of the path which are machine name and share name), and there is \.\
type nonsense that can exist in a path.
我自己正在尝试一个非常幼稚的实现,这似乎是一个字符串处理的噩梦。有/
vs \
,有网络共享的特殊情况,它以开头\\
(你也不能尝试 mkdir() 路径的前两级,即机器名和共享名),并且\.\
可以存在类型废话在一个路径中。
Does there exist a simple way to do this in C++?
在 C++ 中是否存在一种简单的方法来做到这一点?
采纳答案by eran
Note: this answer is somewhat quick and dirty, and doesn't handle all cases. If this is ok with you, read on. If not, consider using one of the other options.
注意:这个答案有点快速和肮脏,并且不能处理所有情况。如果这对你没问题,请继续阅读。如果没有,请考虑使用其他选项之一。
You can use good old mkdir for that. Just run
您可以为此使用旧的 mkdir。赶紧跑
system("mkdir " + strPath);
and you're done.
你就完成了。
Well, almost. There are still cases you have to take care of, such as network shares (which might not work) and backslashes. But when using relatively safe paths, you can use this shorter form.
嗯,差不多。仍有一些情况您必须处理,例如网络共享(可能不起作用)和反斜杠。但是当使用相对安全的路径时,您可以使用这种较短的形式。
Another thing you might find useful getting rid of the possible nuisance is _fullpath(), which will resolve the given path into a full and clean one. Knowing you have a clean path, you should have no problem writing a rather trivial recursive function that will create the folders one by one, even when dealing with UNC paths.
您可能会发现另一件有用的东西是_fullpath(),它可以将给定的路径解析为完整且干净的路径。知道你有一个干净的路径,你应该可以编写一个相当简单的递归函数来一个一个地创建文件夹,即使在处理 UNC 路径时也是如此。
回答by Frerich Raabe
If you don't need to support Windows versions prior to Windows 2000, you can use the SHCreateDirectoryEx functionfor this. Consider this:
如果您不需要支持 Windows 2000 之前的 Windows 版本,您可以为此使用SHCreateDirectoryEx 函数。考虑一下:
int createDirectoryRecursively( LPCTSTR path )
{
return SHCreateDirectoryEx( NULL, path, NULL );
}
// ...
if ( createDirectoryRecursively( T("C:\Foo\Bar\Baz") ) == ERROR_SUCCESS ) {
// Bingo!
}
In case using such shell32.dll API ever becomes an issue, you can always reimplement the createDirectoryRecursively function above with something else (possibly a hand-wired loop).
如果使用这样的 shell32.dll API 成为一个问题,你总是可以用其他东西(可能是手动循环)重新实现上面的 createDirectoryRecursively 函数。
回答by ctacke
Here's a version that works with no external libraries, so Win32-only, and that function in all versions of Windows (including Windows CE, where I needed it):
这是一个不使用外部库的版本,因此仅适用于 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'\');
}
回答by Jonathan Feucht
Here is a function I wrote which iteratively creates a folder tree. Here is the main function:
这是我编写的一个函数,它迭代地创建一个文件夹树。这是主要功能:
#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;
}
The folderExists
routine is as follows:
该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;
}
An example call I tested the above functions with is as follows (and it works):
我测试上述函数的示例调用如下(并且它有效):
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");
This function hasn't gone through very thorough testing, and I'm not sure it yet works with other operating systems (but probably is compatible with a few modifications). I am currently using Visual Studio 2010
with Windows 7.
此功能尚未经过非常彻底的测试,我不确定它是否适用于其他操作系统(但可能与一些修改兼容)。我现在使用的Visual Studio 2010
带Windows 7.
回答by Aamir
SHCreateDirectoryfunction can do this. But the document states that it can get deprecated in later version of Windows.
SHCreateDirectory函数可以做到这一点。但该文档指出,它可能会在更高版本的 Windows 中被弃用。
From MSDN
来自 MSDN
Note This function is available through Windows XP Service Pack 2 (SP2) and Microsoft Windows Server 2003. It might be altered or unavailable in subsequent versions of Windows.
注意 此功能可通过 Windows XP Service Pack 2 (SP2) 和 Microsoft Windows Server 2003 使用。它可能会在 Windows 的后续版本中更改或不可用。
回答by sailfish009
handy and simple working version of 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;
}
回答by Nathan Reed
With C++17, this can be done quite easily using std::filesystem::create_directories()
.
使用 C++17,这可以很容易地使用std::filesystem::create_directories()
.
Example:
例子:
#include <filesystem>
...
const char* path = "C:\foo\bar";
std::filesystem::create_directories(path);
回答by xezon
For Windows XP and up. Expects widechar null terminated string and amount of recursive actions as parameters. Was not tested with more than 1 level yet.
对于 Windows XP 及更高版本。需要宽字符空终止字符串和递归操作的数量作为参数。尚未测试超过 1 个级别。
Note: Path seperators must be '\'
注意:路径分隔符必须是“\”
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;
};
回答by Tommi
I'm modifying an old Windows CE app, and this is what I'm planning to use. Should work in Windows CE, too. This is actually recursive, too:
我正在修改一个旧的 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);
}
}
回答by steel17
ctacke You forgot the last segment. e.g. '\aa\bb\"cc"' Following is the modification for ctacke:
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;
}
}
}
回答by rebeldevil14
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;