Comment créer une arborescence de répertoires en C++ / Linux?

je veux un moyen facile de créer plusieurs répertoires en C++/Linux.

par exemple, je veux enregistrer un fichier lola.fichier dans le répertoire:

/tmp/a/b/c

mais si les répertoires ne sont pas là, je veux qu'ils soient créés automatiquement. Un exemple serait parfait.

87
demandé sur Lipis 2009-03-23 23:10:26

14 réponses

Voici une fonction C qui peut être compilée avec des compilateurs C++.

/*
@(#)File:           $RCSfile: mkpath.c,v $
@(#)Version:        $Revision: 1.13 $
@(#)Last changed:   $Date: 2012/07/15 00:40:37 $
@(#)Purpose:        Create all directories in path
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1990-91,1997-98,2001,2005,2008,2012
*/

/*TABSTOP=4*/

#include "jlss.h"
#include "emalloc.h"

#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <string.h>
#include "sysstat.h"    /* Fix up for Windows - inc mode_t */

typedef struct stat Stat;

#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_mkpath_c[] = "@(#)$Id: mkpath.c,v 1.13 2012/07/15 00:40:37 jleffler Exp $";
#endif /* lint */

static int do_mkdir(const char *path, mode_t mode)
{
    Stat            st;
    int             status = 0;

    if (stat(path, &st) != 0)
    {
        /* Directory does not exist. EEXIST for race condition */
        if (mkdir(path, mode) != 0 && errno != EEXIST)
            status = -1;
    }
    else if (!S_ISDIR(st.st_mode))
    {
        errno = ENOTDIR;
        status = -1;
    }

    return(status);
}

/**
** mkpath - ensure all directories in path exist
** Algorithm takes the pessimistic view and works top-down to ensure
** each directory in path exists, rather than optimistically creating
** the last element and working backwards.
*/
int mkpath(const char *path, mode_t mode)
{
    char           *pp;
    char           *sp;
    int             status;
    char           *copypath = STRDUP(path);

    status = 0;
    pp = copypath;
    while (status == 0 && (sp = strchr(pp, '/')) != 0)
    {
        if (sp != pp)
        {
            /* Neither root nor double slash in path */
            *sp = '"151900920"';
            status = do_mkdir(copypath, mode);
            *sp = '/';
        }
        pp = sp + 1;
    }
    if (status == 0)
        status = do_mkdir(path, mode);
    FREE(copypath);
    return (status);
}

#ifdef TEST

#include <stdio.h>

/*
** Stress test with parallel running of mkpath() function.
** Before the EEXIST test, code would fail.
** With the EEXIST test, code does not fail.
**
** Test shell script
** PREFIX=mkpath.$$
** NAME=./$PREFIX/sa/32/ad/13/23/13/12/13/sd/ds/ww/qq/ss/dd/zz/xx/dd/rr/ff/ff/ss/ss/ss/ss/ss/ss/ss/ss
** : ${MKPATH:=mkpath}
** ./$MKPATH $NAME &
** [...repeat a dozen times or so...]
** ./$MKPATH $NAME &
** wait
** rm -fr ./$PREFIX/
*/

int main(int argc, char **argv)
{
    int             i;

    for (i = 1; i < argc; i++)
    {
        for (int j = 0; j < 20; j++)
        {
            if (fork() == 0)
            {
                int rc = mkpath(argv[i], 0777);
                if (rc != 0)
                    fprintf(stderr, "%d: failed to create (%d: %s): %s\n",
                            (int)getpid(), errno, strerror(errno), argv[i]);
                exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
            }
        }
        int status;
        int fail = 0;
        while (wait(&status) != -1)
        {
            if (WEXITSTATUS(status) != 0)
                fail = 1;
        }
        if (fail == 0)
            printf("created: %s\n", argv[i]);
    }
    return(0);
}

#endif /* TEST */

macros STRDUP() et FREE() erreur de vérification des versions de strdup() et free() , a déclaré dans emalloc.h (et mis en œuvre dans emalloc.c et estrdup.c ). L'en-tête "sysstat.h" traite des versions brisées de <sys/stat.h> et peut être remplacé par <sys/stat.h> sur les systèmes Unix modernes (mais il y a eu de nombreux problèmes en 1990). Et "jlss.h" déclare mkpath() .

le changement entre v1.12 (précédent) et v1.13 (ci-dessus) est l'essai pour EEXIST dans do_mkdir() . Cela a été souligné comme nécessaire par Switch - Merci, Switch. Le code de test a été mis à jour et reproduit le problème sur un MacBook Pro (2.3 GHz Intel Core i7, tournant Mac OS X 10.7.4), et suggère que le problème est corrigé dans la révision (mais les tests ne peuvent montrer que la présence de bogues, jamais leur absence.)

(vous êtes par la présente autorisé à utiliser ce code à toute fin avec attribution.)

54
répondu Jonathan Leffler 2017-05-23 12:10:05

facile avec Boost.Système de fichiers: create_directories

#include <boost/filesystem.hpp>
//...
boost::filesystem::create_directories("/tmp/a/b/c");

retourne: true si un nouveau répertoire a été créé, sinon false .

145
répondu Benoît 2015-01-22 02:01:07
system("mkdir -p /tmp/a/b/c")

est le chemin le plus court je pense (en termes de longueur de code, pas nécessairement le temps d'exécution).

ce n'est pas multiplate-forme mais fonctionnera sous Linux.

34
répondu ChristopheD 2017-02-03 13:39:59
#include <sys/types.h>
#include <sys/stat.h>

int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

à Partir de ici . Vous devrez peut-être faire des mkdirs distincts pour /tmp, /tmp/a, /tmp/a/b/ et then /tmp/A/b/C parce qu'il n'y a pas d'équivalent du drapeau-p dans l'api C. Soyez sûr et ignorez les EEXISTS errno pendant que vous faites les niveaux supérieurs.

25
répondu Paul Tomblin 2009-03-23 20:21:16

Voici mon exemple de code (il fonctionne à la fois pour Windows et 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;
}

Utilisation:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
17
répondu Maxim Suslov 2015-04-23 16:08:06

vous avez dit " C++ "mais tout le monde ici semble penser" Bash shell."

vérifiez le code source de gnu mkdir ; ensuite vous pouvez voir comment implémenter les commandes shell en C++.

7
répondu Jason Cohen 2009-03-23 20:15:56

c'est similaire au précédent mais fonctionne en avant à travers la chaîne au lieu d'en arrière récursivement. Laisse errno avec la bonne valeur pour la dernière défaillance. S'il y a une barre oblique, il y a un temps supplémentaire dans la boucle qui aurait pu être évité via un find_first_of() en dehors de la boucle ou en détectant la barre oblique / et le paramètre pre à 1. L'efficacité est la même que l'on soit mis en place par une première boucle ou un appel pré-boucle, et la complexité serait (légèrement) plus élevée quand en utilisant l'appel pré-boucle.

#include <iostream>
#include <string>
#include <sys/stat.h>

int
mkpath(std::string s,mode_t mode)
{
    size_t pre=0,pos;
    std::string dir;
    int mdret;

    if(s[s.size()-1]!='/'){
        // force trailing / so we can handle everything in loop
        s+='/';
    }

    while((pos=s.find_first_of('/',pre))!=std::string::npos){
        dir=s.substr(0,pos++);
        pre=pos;
        if(dir.size()==0) continue; // if leading / first time is 0 length
        if((mdret=mkdir(dir.c_str(),mode)) && errno!=EEXIST){
            return mdret;
        }
    }
    return mdret;
}

int main()
{
    int mkdirretval;
    mkdirretval=mkpath("./foo/bar",0755);
    std::cout << mkdirretval << '\n';

}
6
répondu Patrick 2012-10-15 21:16:14
bool mkpath( std::string path )
{
    bool bSuccess = false;
    int nRC = ::mkdir( path.c_str(), 0775 );
    if( nRC == -1 )
    {
        switch( errno )
        {
            case ENOENT:
                //parent didn't exist, try to create it
                if( mkpath( path.substr(0, path.find_last_of('/')) ) )
                    //Now, try to create again.
                    bSuccess = 0 == ::mkdir( path.c_str(), 0775 );
                else
                    bSuccess = false;
                break;
            case EEXIST:
                //Done!
                bSuccess = true;
                break;
            default:
                bSuccess = false;
                break;
        }
    }
    else
        bSuccess = true;
    return bSuccess;
}
3
répondu Mark 2012-07-06 17:27:47

j'ai Donc besoin de mkdirp() aujourd'hui, et a trouvé des solutions sur cette page, trop compliqué. Donc j'ai écrit un assez court extrait, qui facilement être copié pour les autres qui trébuchez sur ce fil une merveille pourquoi nous avons besoin de tant de lignes de code.

mkdirp.h

#ifndef MKDIRP_H
#define MKDIRP_H

#include <sys/stat.h>

#define DEFAULT_MODE      S_IRWXU | S_IRGRP |  S_IXGRP | S_IROTH | S_IXOTH

/** Utility function to create directory tree */
bool mkdirp(const char* path, mode_t mode = DEFAULT_MODE);

#endif // MKDIRP_H

mkdirp.cpp

#include <errno.h>

bool mkdirp(const char* path, mode_t mode) {
  // const cast for hack
  char* p = const_cast<char*>(path);

  // Do mkdir for each slash until end of string or error
  while (*p != '"151910920"') {
    // Skip first character
    p++;

    // Find first slash or end
    while(*p != '"151910920"' && *p != '/') p++;

    // Remember value from p
    char v = *p;

    // Write end of string at p
    *p = '"151910920"';

    // Create folder from path to '"151910920"' inserted at p
    if(mkdir(path, mode) == -1 && errno != EEXIST) {
      *p = v;
      return false;
    }

    // Restore path to it's former glory
    *p = v;
  }

  return true;
}

si vous n'aimez pas le montage et la modification temporaire string, fais juste un strdup() et free() après.

3
répondu jonasfj 2015-03-28 08:18:38

étant donné que ce message est classé haut dans Google pour" créer L'arborescence des répertoires", je vais poster une réponse qui fonctionnera pour Windows - cela fonctionnera en utilisant L'API Win32 compilé pour UNICODE ou MBCS. C'est porté à partir du code de Mark ci-dessus.

Puisqu'il s'agit de Windows avec lequel nous travaillons, les séparateurs de répertoires sont des barres obliques, pas des barres obliques. Si vous préférez les barres obliques, changez '\' en '/'

il fonctionnera avec:

c:\foo\bar\hello\world

et

c:\foo\bar\hellp\world\

(i.e.: n'a pas besoin de slash arrière, de sorte que vous n'avez pas à vérifier pour elle.)

avant de dire "il suffit d'utiliser SHCreateDirectoryEx () dans Windows", notez que SHCreateDirectoryEx () est déprécié et peut être supprimé à tout moment des versions futures de Windows.

bool CreateDirectoryTree(LPCTSTR szPathTree, LPSECURITY_ATTRIBUTES lpSecurityAttributes = NULL){
    bool bSuccess = false;
    const BOOL bCD = CreateDirectory(szPathTree, lpSecurityAttributes);
    DWORD dwLastError = 0;
    if(!bCD){
        dwLastError = GetLastError();
    }else{
        return true;
    }
    switch(dwLastError){
        case ERROR_ALREADY_EXISTS:
            bSuccess = true;
            break;
        case ERROR_PATH_NOT_FOUND:
            {
                TCHAR szPrev[MAX_PATH] = {0};
                LPCTSTR szLast = _tcsrchr(szPathTree,'\');
                _tcsnccpy(szPrev,szPathTree,(int)(szLast-szPathTree));
                if(CreateDirectoryTree(szPrev,lpSecurityAttributes)){
                    bSuccess = CreateDirectory(szPathTree,lpSecurityAttributes)!=0;
                    if(!bSuccess){
                        bSuccess = (GetLastError()==ERROR_ALREADY_EXISTS);
                    }
                }else{
                    bSuccess = false;
                }
            }
            break;
        default:
            bSuccess = false;
            break;
    }

    return bSuccess;
}
2
répondu Andy 2013-01-09 15:22:51

je sais que c'est une vieille question mais elle apparaît haut sur les résultats de recherche de google et les réponses fournies ici ne sont pas vraiment en C++ ou sont un peu trop compliquées.

veuillez noter que dans mon exemple createDirTree() est très simple car tout le levage lourd (vérification des erreurs, validation du chemin) doit être fait par createDir() de toute façon. De plus, createDir() devrait retourner true si directory existe déjà ou si tout ne fonctionne pas.

Voici comment je ferait cela en C++:

#include <iostream>
#include <string>

bool createDir(const std::string dir)
{
    std::cout << "Make sure dir is a valid path, it does not exist and create it: "
              << dir << std::endl;
    return true;
}

bool createDirTree(const std::string full_path)
{
    size_t pos = 0;
    bool ret_val = true;

    while(ret_val == true && pos != std::string::npos)
    {
        pos = full_path.find('/', pos + 1);
        ret_val = createDir(full_path.substr(0, pos));
    }

    return ret_val;
}

int main()
{
    createDirTree("/tmp/a/b/c");
    return 0;
}

bien sûr la fonction createDir() sera spécifique au système et il y a déjà assez d'exemples dans d'autres réponses comment l'écrire pour linux, donc j'ai décidé de la Sauter.

2
répondu Tom 2014-01-30 09:31:20

si dir n'existe pas, créer:

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
1
répondu Frank 2016-12-07 23:01:37
mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending
0
répondu Tim Cooper 2011-06-29 00:20:02

les autres t'ont donné la bonne réponse, mais j'ai pensé te montrer une autre chose que tu peux faire:

mkdir -p /tmp/a/{b,c}/d

va créer les chemins suivants:

/tmp/a/b/d
/tmp/a/c/d

les accolades vous permettent de créer plusieurs répertoires à la fois sur le même niveau de la hiérarchie, tandis que l'option -p signifie"créer des répertoires parents selon les besoins".

-1
répondu rmeador 2009-03-23 20:15:35