J'essaie de créer une fonction qui prend le nom d'un répertoire (C:\foo\bar
, ou ..\foo\bar\..\baz
, ou \\someserver\foo\bar
), et crée des répertoires si nécessaire afin que le chemin d'accès complet soit créé.
J'essaie moi-même une implémentation assez naïve et cela semble être un cauchemar pour le traitement des chaînes. Il y a /
contre \
, il y a le cas particulier des partages réseau qui commencent par \\
(vous ne pouvez pas non plus tenter de mkdir () les deux premiers niveaux du chemin qui sont le nom de la machine et le nom du partage), et il y a \.\
saisit un non-sens qui peut exister dans un chemin.
Existe-t-il un moyen simple de le faire en C++?
Si vous n'avez pas besoin de prendre en charge les versions de Windows antérieures à Windows 2000, vous pouvez utiliser la fonction SHCreateDirectoryEx pour cela. Considère ceci:
int createDirectoryRecursively( LPCTSTR path )
{
return SHCreateDirectoryEx( NULL, path, NULL );
}
// ...
if ( createDirectoryRecursively( T("C:\\Foo\\Bar\\Baz") ) == ERROR_SUCCESS ) {
// Bingo!
}
Dans le cas où l'utilisation d'une telle API Shell32.dll devient un problème, vous pouvez toujours réimplémenter la fonction createDirectoryRecursively ci-dessus avec autre chose (éventuellement une boucle câblée à la main).
Voici une version qui fonctionne sans bibliothèque externe, donc Win32 uniquement, et qui fonctionne dans toutes les versions de Windows (y compris Windows CE, où j'en avais besoin):
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'\\');
}
Voici une fonction que j'ai écrite qui crée de manière itérative une arborescence de dossiers. Voici la fonction principale:
#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;
}
La routine folderExists
est la suivante:
// 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;
}
Un exemple d'appel avec lequel j'ai testé les fonctions ci-dessus est le suivant (et cela fonctionne):
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");
Cette fonction n'a pas subi de tests très approfondis, et je ne suis pas sûr qu'elle fonctionne encore avec d'autres systèmes d'exploitation (mais est probablement compatible avec quelques modifications). J'utilise actuellement Visual Studio 2010
avec Windows 7.
SHCreateDirectory la fonction peut le faire. Mais le document indique qu'il peut devenir obsolète dans une version ultérieure de Windows.
Depuis MSDN
Remarque Cette fonction est disponible via Windows XP Service Pack 2 (SP2) et Microsoft Windows Server 2003. Elle peut être modifiée ou indisponible dans les versions ultérieures de Windows.
version de travail pratique et simple de la mienne:
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;
}
Pour Windows XP et plus. Attend la chaîne terminée par widechar null et la quantité d'actions récursives en tant que paramètres. N'a pas encore été testé avec plus d'un niveau.
Remarque: les séparateurs de chemin doivent être '\'
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;
};
Je modifie une ancienne application Windows CE, et c'est ce que je prévois d'utiliser. Devrait également fonctionner dans Windows CE. C'est aussi récursif:
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);
}
}
ctacke Vous avez oublié le dernier segment. par exemple. '\ aa\bb\"cc"' Voici la modification pour 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;
}
}
}
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;