Как я могу создать дерево каталогов в C ++ /Linux?

Я хочу простой способ создания нескольких каталогов в C ++ /Linux.

Например, я хочу сохранить файл lola.file в каталоге:

/tmp/a/b/c

но если каталогов нет, я хочу, чтобы они создавались автоматически. Рабочий пример был бы идеальным.

90 голосов | спросил Lipis 23 MarpmMon, 23 Mar 2009 23:10:26 +03002009-03-23T23:10:26+03:0011 2009, 23:10:26

13 ответов


0

Вот функция C, которую можно скомпилировать с помощью компиляторов 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 = '\0';
            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 */

Макросы STRDUP() и FREE() являются версиями strdup() и free()для проверки ошибок >, объявленный в emalloc.h (и реализованный в emalloc.c и estrdup.c). Заголовок "sysstat.h" имеет дело с неработающими версиями <sys/stat.h> и может быть заменен на <sys/stat.h> в современных системах Unix (но в 1990 году было много проблем). И "jlss.h" объявляет mkpath().

Изменение между v1.12 (предыдущим) и v1.13 (выше) является тестом для EEXIST в do_mkdir(). На это указывало по необходимости Switch - спасибо, Switch. Тестовый код был обновлен и воспроизвел проблему на MacBook Pro (Intel Core i7 с частотой 2,3 ГГц, работает под управлением Mac OS X 10.7.4) и предполагает, что проблема исправлена ​​в ревизии (но тестирование может показать только наличие ошибок). , никогда их отсутствие).

(Настоящим вам разрешается использовать этот код для любых целей с указанием авторства.)

ответил Jonathan Leffler 23 MarpmMon, 23 Mar 2009 23:52:50 +03002009-03-23T23:52:50+03:0011 2009, 23:52:50
0

Просто с Boost.Filesystem: create_directories

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

Возвращает: true, если был создан новый каталог, в противном случае false.

ответил Benoît 23 MarpmMon, 23 Mar 2009 23:29:58 +03002009-03-23T23:29:58+03:0011 2009, 23:29:58
0
system("mkdir -p /tmp/a/b/c")

- это самый короткий способ, который я могу придумать (с точки зрения длины кода, не обязательно времени выполнения).

Он не кроссплатформенный, но будет работать под Linux.

ответил ChristopheD 23 MarpmMon, 23 Mar 2009 23:12:34 +03002009-03-23T23:12:34+03:0011 2009, 23:12:34
0

Вот мой пример кода (он работает как для 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;
}

Использование:

$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
ответил Maxim Suslov 23 PMpThu, 23 Apr 2015 19:08:06 +030008Thursday 2015, 19:08:06
0

Вы сказали «C ++», но все здесь, кажется, думают «Bash shell».

Посмотрите исходный код для gnu mkdir; тогда вы можете увидеть, как реализовать команды оболочки в C ++.

ответил Jason Cohen 23 MarpmMon, 23 Mar 2009 23:15:56 +03002009-03-23T23:15:56+03:0011 2009, 23:15:56
0

Это похоже на предыдущий, но работает вперед через строку, а не рекурсивно назад. Оставляет errno с правильным значением для последнего отказа. Если есть начальная косая черта, есть дополнительное время в цикле, которого можно было бы избежать с помощью одного find_first_of () вне цикла или путем обнаружения ведущего /и установки pre в 1. Эффективность одинакова, независимо от того, настроим ли мы первый цикл или вызов предварительного цикла, и сложность будет (немного) выше при использовании вызова предварительного цикла.

#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';

}
ответил Patrick 16 +04002012-10-16T01:16:14+04:00312012bEurope/MoscowTue, 16 Oct 2012 01:16:14 +0400 2012, 01:16:14
0
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;
}
ответил Mark 6 J000000Friday12 2012, 21:27:47
0

Поскольку эта публикация занимает в Google высокий рейтинг как «Создать дерево каталогов», я собираюсь опубликовать ответ, который будет работать для Windows - он будет работать с использованием Win32 API, скомпилированного для UNICODE или MBCS. Это перенесено из кода выше Марка.

Так как это Windows, с которой мы работаем, разделители каталогов - это BACK-слэши, а не прямые. Если вы предпочитаете использовать косую черту, измените '\\' на '/'

Он будет работать с:

c:\foo\bar\hello\world

и

c:\foo\bar\hellp\world\

(т. е. не нужно использовать косую черту, поэтому вам не нужно проверять это.)

Прежде чем сказать «Просто используйте SHCreateDirectoryEx () в Windows», обратите внимание, что SHCreateDirectoryEx () устарела и может быть удалена в любое время из будущих версий 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;
}
ответил Andy 12 WedEurope/Moscow2012-12-12T02:16:36+04:00Europe/Moscow12bEurope/MoscowWed, 12 Dec 2012 02:16:36 +0400 2012, 02:16:36
0

Я знаю, что это старый вопрос, но он показывается высоко в результатах поиска Google, и ответы, представленные здесь, не совсем на C ++ или слишком сложны.

Обратите внимание, что в моем примере createDirTree () очень прост, потому что вся тяжелая работа (проверка ошибок, проверка пути) должна быть выполнена в любом случае createDir (). Кроме того, createDir () должен возвращать true, если каталог уже существует, или все это не будет работать.

Вот как я это сделаю в 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;
}

Конечно, функция createDir () будет зависеть от системы, и в других ответах уже достаточно примеров, как написать ее для linux, поэтому я решил ее пропустить.

ответил Tom 29 Jpm1000000pmWed, 29 Jan 2014 21:09:20 +040014 2014, 21:09:20
0

Если dir не существует, создайте его:

boost::filesystem::create_directories(boost::filesystem::path(output_file).parent_path().string().c_str()); 
ответил Frank 8 ThuEurope/Moscow2016-12-08T00:53:13+03:00Europe/Moscow12bEurope/MoscowThu, 08 Dec 2016 00:53:13 +0300 2016, 00:53:13
0

Следует отметить, что начиная с C ++ 17 интерфейс файловой системы является частью стандартной библиотеки. Это означает, что для создания каталогов можно использовать следующее:

#include <filesystem>

std::filesystem::create_directories("/a/b/c/d")

Дополнительная информация здесь: https://en.cppreference.com/w/cpp /файловой системы /create_directory

Кроме того, с gcc нужно "-std = c ++ 17" для CFLAGS. И "-lstdc ++ fs" для LDLIBS. Последнее потенциально не потребуется в будущем.

ответил mcsim 29 +03002018-10-29T21:13:45+03:00312018bEurope/MoscowMon, 29 Oct 2018 21:13:45 +0300 2018, 21:13:45
0
mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending
ответил 23 MarpmMon, 23 Mar 2009 23:12:59 +03002009-03-23T23:12:59+03:0011 2009, 23:12:59
0

Остальные дали вам правильный ответ, но я подумал, что продемонстрирую еще одну полезную вещь, которую вы можете сделать:

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

создаст следующие пути:

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

Скобки позволяют вам создавать несколько каталогов одновременно на одном и том же уровне иерархии, тогда как опция -p означает «создать родителя». каталоги по мере необходимости ".

ответил rmeador 23 MarpmMon, 23 Mar 2009 23:15:35 +03002009-03-23T23:15:35+03:0011 2009, 23:15:35

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132