Как я рекурсивно создаю папку в Win32?
Я пытаюсь создать функцию, которая принимает имя каталога (C:\foo\bar
или ..\foo\bar\..\baz
или \\someserver\foo\bar
) и создает необходимые каталоги, чтобы создать весь путь.
Я пытаюсь реализовать эту наивную реализацию, и это похоже на кошмар для обработки строк. Существует /
vs \
, есть частный случай сетевых долей, которые начинаются с \\
(также вы не можете пытаться mkdir() первые два уровня пути, которые являются машинным именем и общим именем), и существует бессмысленность типа \.\
, которая может существовать в пути.
Существует ли простой способ сделать это в С++?
Ответы
Ответ 1
Примечание: этот ответ несколько быстрый и грязный, и он не обрабатывает все случаи. Если с вами все в порядке, читайте дальше. Если нет, рассмотрите возможность использования одного из других вариантов.
Для этого вы можете использовать добрый старый mkdir. Просто запустите
system("mkdir " + strPath);
и все готово.
Ну, почти. Есть еще случаи, о которых вам нужно позаботиться, например, об общих долях сети (которые могут не сработать) и обратных косых чертах. Но при использовании относительно безопасных путей вы можете использовать эту более короткую форму.
Еще одна вещь, которую вы можете найти полезной, избавиться от возможной неприятности - _ fullpath(), которая разрешит данный путь в полный и чистый. Зная, что у вас чистый путь, вам не составит труда написать довольно тривиальную рекурсивную функцию, которая будет создавать папки один за другим, даже при работе с UNC-путями.
Ответ 2
Если вам не нужно поддерживать версии Windows до Windows 2000, вы можете использовать функцию SHCreateDirectoryEx. Рассмотрим это:
int createDirectoryRecursively( LPCTSTR path )
{
return SHCreateDirectoryEx( NULL, path, NULL );
}
// ...
if ( createDirectoryRecursively( T("C:\\Foo\\Bar\\Baz") ) == ERROR_SUCCESS ) {
// Bingo!
}
В случае использования такого API-интерфейса shell32.dll когда-либо становится проблемой, вы всегда можете переопределить функцию createDirectoryRecursive выше с чем-то другим (возможно, с проводкой в ручном режиме).
Ответ 3
Здесь версия, которая работает без внешних библиотек, так что Win32-only и эта функция работают во всех версиях 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'\\');
}
Ответ 4
Вот функция, которую я написал, которая итеративно создает дерево папок. Вот основная функция:
#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 2010
с Windows 7.
Ответ 5
Функция SHCreateDirectory может это сделать. Но в документе говорится, что он может устареть в более поздней версии Windows.
Из MSDN
Примечание. Эта функция доступный через службу Windows XP Pack 2 (SP2) и Microsoft Windows Server 2003. Он может быть изменен или недоступны в последующих версиях Windows.
Ответ 6
удобная и простая рабочая версия моей:
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;
}
Ответ 7
Для Windows XP и выше. Ожидает, что строка с нулевым символом завершена и количество рекурсивных действий в качестве параметров. Не тестировалось с более чем 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;
};
Ответ 8
Использовать Boost:: файловую систему
эта ссылка вам поможет:)
http://www.boost.org/doc/libs/1_40_0/libs/filesystem/doc/index.htm
Ответ 9
ctacke Вы забыли последний сегмент. например "\ Аа\бб \„куб.см“"
Ниже приведена модификация для 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;
}
}
}
Ответ 10
Я изменяю старое приложение 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);
}
}
Ответ 11
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;
Ответ 12
В С++ 17 это легко сделать с помощью std::filesystem::create_directories()
.
Пример:
#include <filesystem>
...
const char* path = "C:\\foo\\bar";
std::filesystem::create_directories(path);
Ответ 13
От 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;
Это должно дать вам представление о том, как обращаться с строкой пути. Затем после этого все, что вам нужно сделать, - это цикл через пути, начиная с диска и заканчивая самой глубокой папкой. Проверьте, существует ли папка, а если нет, создайте ее.
Ответ 14
Вот мой пример кода (скопирован из Как создать дерево каталогов на С++/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
Ответ 15
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
}
}
}