如何在 C++/Linux 中创建目录树?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/675039/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:09:08  来源:igfitidea点击:

How can I create directory tree in C++/Linux?

c++linuxdirectory

提问by Lipis

I want an easy way to create multiple directories in C++/Linux.

我想要一种在 C++/Linux 中创建多个目录的简单方法。

For example I want to save a file lola.file in the directory:

例如我想在目录中保存一个文件 lola.file :

/tmp/a/b/c

but if the directories are not there I want them to be created automagically. A working example would be perfect.

但如果目录不存在,我希望它们自动创建。一个工作示例将是完美的。

采纳答案by Jonathan Leffler

Here's a C function that can be compiled with C++ compilers.

这是一个可以用 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 = '
system("mkdir -p /tmp/a/b/c")
'; 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 */

The macros STRDUP()and FREE()are error-checking versions of strdup()and free(), declared in emalloc.h(and implemented in emalloc.cand estrdup.c). The "sysstat.h"header deals with broken versions of <sys/stat.h>and can be replaced by <sys/stat.h>on modern Unix systems (but there were many issues back in 1990). And "jlss.h"declares mkpath().

STRDUP()andFREE()是and的错误检查版本,在strdup()andfree()中声明emalloc.h(并在emalloc.cand 中实现estrdup.c)。该"sysstat.h"用的破碎版本头交易<sys/stat.h>,并且可以被替换为<sys/stat.h>现代Unix系统上(但也有许多问题在1990年)。并"jlss.h"声明mkpath()

The change between v1.12 (previous) and v1.13 (above) is the test for EEXISTin do_mkdir(). This was pointed out as necessary by Switch— thank you, Switch. The test code has been upgraded and reproduced the problem on a MacBook Pro (2.3GHz Intel Core i7, running Mac OS X 10.7.4), and suggests that the problem is fixed in the revision (but testing can only show the presence of bugs, never their absence).

v1.12(以前)和 v1.13(上面)之间的变化是对EEXISTin的测试do_mkdir()Switch指出这是必要的——谢谢你,Switch。测试代码已在MacBook Pro(2.3GHz Intel Core i7,运行Mac OS X 10.7.4)上升级并复现问题,并提示该问题已在修订版中修复(但测试只能显示存在错误,从不缺席)。

(You are hereby given permission to use this code for any purpose with attribution.)

(特此允许您出于任何目的使用此代码并注明出处。)

回答by ChristopheD

mkdir -p /dir/to/the/file

touch /dir/to/the/file/thefile.ending

is the shortest way I can think of (in terms of the length of code, not necessarily execution time).

是我能想到的最短方法(就代码长度而言,不一定是执行时间)。

It's not cross-platform but will work under Linux.

它不是跨平台的,但可以在 Linux 下工作。

回答by ChristopheD

#include <sys/types.h>
#include <sys/stat.h>

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

回答by Paul Tomblin

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

From here. You may have to do separate mkdirs for /tmp, /tmp/a, /tmp/a/b/ and then /tmp/a/b/c because there isn't an equivalent of the -p flag in the C api. Be sure and ignore the EEXISTS errno while you're doing the upper level ones.

这里开始。您可能必须为 /tmp、/tmp/a、/tmp/a/b/ 和 /tmp/a/b/c 分别执行单独的 mkdir,因为 C api 中没有与 -p 标志等效的标志。当你在做上层的时候,一定要忽略 EEXISTS errno。

回答by rmeador

The others got you the right answer, but I thought I'd demonstrate another neat thing you can do:

其他人给了你正确的答案,但我想我会展示你可以做的另一件事:

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

Will create the following paths:

将创建以下路径:

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

The braces allow you to create multiple directories at once on the same level of the hierarchy, whereas the -poption means "create parent directories as needed".

大括号允许您在层次结构的同一级别上一次创建多个目录,而该-p选项的意思是“根据需要创建父目录”。

回答by Jason Cohen

You said "C++" but everyone here seems to be thinking "Bash shell."

您说的是“C++”,但这里的每个人似乎都在考虑“Bash shell”。

Check out the source code to gnu mkdir; then you can see how to implement the shell commands in C++.

查看 gnu 的源代码mkdir;然后你可以看到如何在 C++ 中实现 shell 命令。

回答by Beno?t

Easy with Boost.Filesystem: create_directories

使用 Boost.Filesystem 很容易: create_directories

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;
}

Returns: trueif a new directory was created, otherwise false.

返回:true如果创建了新目录,否则false.

回答by Mark

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

int
mkpath(std::string s,mode_t mode)
{
    size_t pos=0;
    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('/',pos))!=std::string::npos){
        dir=s.substr(0,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';

}

回答by phorgan1

This is similar to the previous but works forward through the string instead of recursively backwards. Leaves errno with the right value for last failure. If there's a leading slash, there's an extra time through the loop which could have been avoided via one find_first_of() outside the loop or by detecting the leading / and setting pre to 1. The efficiency is the same whether we get set up by a first loop or a pre loop call, and the complexity would be (slightly) higher when using the pre-loop call.

这与前面的类似,但通过字符串向前工作,而不是向后递归。将 errno 保留为上次失败的正确值。如果有前导斜杠,则循环中会有额外的时间,这可以通过循环外的 find_first_of() 或通过检测前导 / 并将 pre 设置为 1 来避免。第一个循环或预循环调用,使用预循环调用时复杂性会(略)高。

c:\foo\bar\hello\world

回答by Andy

Since this post is ranking high in Google for "Create Directory Tree", I am going to post an answer that will work for Windows — this will work using Win32 API compiled for UNICODE or MBCS. This is ported from Mark's code above.

由于这篇文章在 Google 的“创建目录树”中排名靠前,我将发布一个适用于 Windows 的答案——这将使用为 UNICODE 或 MBCS 编译的 Win32 API 工作。这是从上面 Mark 的代码移植过来的。

Since this is Windows we are working with, directory separators are BACK-slashes, not forward slashes. If you would rather have forward slashes, change '\\'to '/'

由于这是我们正在使用的 Windows,目录分隔符是反斜杠,而不是正斜杠。如果您希望使用正斜杠,请更改'\\''/'

It will work with:

它将与:

c:\foo\bar\hellp\world\

and

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;
}

(i.e.: does not need trailing slash, so you don't have to check for it.)

(即:不需要尾部斜杠,因此您不必检查它。)

Before saying "Just use SHCreateDirectoryEx()in Windows", note that SHCreateDirectoryEx()is deprecated and could be removed at any time from future versions of Windows.

在说“在 Windows 中只使用SHCreateDirectoryEx()”之前,请注意SHCreateDirectoryEx()已被弃用,并且可以随时从 Windows 的未来版本中删除。

##代码##