C ++ / Linux'ta birden çok dizin oluşturmanın kolay bir yolunu istiyorum.
Örneğin lola.file dosyasını dizine kaydetmek istiyorum:
/tmp/a/b/c
ancak dizinler yoksa otomatik olarak oluşturulmalarını istiyorum. Çalışan bir örnek mükemmel olur.
C ++ / Linux'ta birden çok dizin oluşturmanın kolay bir yolunu istiyorum.
Örneğin lola.file dosyasını dizine kaydetmek istiyorum:
/tmp/a/b/c
ancak dizinler yoksa otomatik olarak oluşturulmalarını istiyorum. Çalışan bir örnek mükemmel olur.
Yanıtlar:
C ++ 17 veya üstü ile,
modern C ++ programlarında kullanılması gereken <filesystem>
işleve std::filesystem::create_directories
sahip standart bir başlık vardır
. Bununla birlikte, C ++ standart işlevlerinin POSIX'e özgü açık izinler (mod) bağımsız değişkeni yoktur.
Ancak, burada C ++ derleyicileriyle derlenebilecek bir C işlevi bulunmaktadır.
/*
@(#)File: mkpath.c
@(#)Purpose: Create all directories in path
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1990-2020
@(#)Derivation: mkpath.c 1.16 2020/06/19 15:08:10
*/
/*TABSTOP=4*/
#include "posixver.h"
#include "mkpath.h"
#include "emalloc.h"
#include <errno.h>
#include <string.h>
/* "sysstat.h" == <sys/stat.h> with fixup for (old) Windows - inc mode_t */
#include "sysstat.h"
typedef struct stat Stat;
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>
#include <unistd.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 */
Makrolar STRDUP()
ve FREE()
, strdup()
ve ' free()
de bildirilen emalloc.h
(ve içinde uygulanan)
emalloc.c
ve hata denetimi sürümleridir
estrdup.c
. "sysstat.h"
Kırık sürümleri ile başlık fırsatlar <sys/stat.h>
ve ile değiştirilebilir <sys/stat.h>
Modern Unix sistemleri üzerinde (ama birçok konu 1990 yılında geri vardı). Ve "mkpath.h"
ilan eder mkpath()
.
V1.12 (cevabın orijinal versiyonu) ve v1.13 (cevabın değiştirilmiş versiyonu) arasındaki değişiklik EEXIST
in
do_mkdir()
. Bu gerekli olduğu için Switch tarafından belirtildi
- teşekkürler, Switch. Test kodu yükseltildi ve sorunu bir MacBook Pro'da (2.3 GHz Intel Core i7, Mac OS X 10.7.4 çalıştıran) yeniden oluşturdu ve sorunun revizyonda düzeltildiğini gösteriyor (ancak testler yalnızca hataların varlığını gösterebilir , asla yoklukları). Gösterilen kod artık v1.16'dır; v1.13'ten bu yana yapılan kozmetik veya idari değişiklikler ( yalnızca test kodunun mkpath.h
yerine jlss.h
ve <unistd.h>
koşulsuz olarak dahil edilmesi gibi ). Alışılmadık şekilde inatçı bir sisteminiz yoksa "sysstat.h"
bunun ile değiştirilmesi gerektiğini
iddia etmek mantıklıdır <sys/stat.h>
.
(Bu kodu atıfta bulunan herhangi bir amaç için kullanma izniniz bulunmaktadır.)
Bu kod,
GitHub'daki SOQ (Stack Overflow Questions) depomda dosyalar olarak mkpath.c
ve
mkpath.h
(vb.)
Src / so-0067-5039
alt dizininde mevcuttur.
if (errno != EEXIST) { status = -1; }
mkdir başarısız olduğunda eklemektir .
stat()
önce kullanmanın sorunu budur mkdir()
; bu bir TOCTOU (kontrol zamanı, kullanım zamanı) problemidir. Arka planda aynı 29 elemanlı yolu oluşturan 13 işlemi çalıştıran bir kabuk betiği ile hatayı gıdıklamayı denedim ve ona ulaşmayı başaramadım. Sonra test programını 20 kez çatallanmak ve her çocuğun denemesini sağlamak için hackledim ve bu hatayı çözmeyi başardı. Sabit kodda olacaktır if (mkdir(path, mode) != 0 && errno != EEXIST) status = -1;
. Bu hatayı göstermiyor.
jlss.h
, başlıklardır ( , emalloc.h
). Ancak, kod benim mevcuttur SOQ dosyaları olarak GitHub'dan (yığın taşması Sorular) deposu jlss.h
, emalloc.c
ve emalloc.h
içinde src / libsoq alt dizininde. Şunlar gerekir posixver.h
de ve birkaç başka ( debug.h
, stderr.c
, stderr.h
- Ne tüm bu dizinde olmalıdır gerekmez bu olduğunu düşünüyorum ama).
Boost.Filesystem ile Kolay: create_directories
#include <boost/filesystem.hpp>
//...
boost::filesystem::create_directories("/tmp/a/b/c");
İade: true
Yeni bir dizin oluşturulmuşsa, aksi halde false
.
The <filesystem> header is not part of C++11; it is a proposal for C++ TR2 based on the Boost.Filesystem library. Visual C++ 2012 includes an implementation of the proposed library.
system("mkdir -p /tmp/a/b/c")
düşünebildiğim en kısa yol (kodun uzunluğu açısından, mutlaka yürütme süresi değil).
Çapraz platform değil ama Linux altında çalışacak.
#include <sys/types.h>
#include <sys/stat.h>
int status;
...
status = mkdir("/tmp/a/b/c", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
Gönderen burada . / Tmp, / tmp / a, / tmp / a / b / ve sonra / tmp / a / b / c için ayrı mkdir'ler yapmanız gerekebilir çünkü C api'de -p bayrağının bir eşdeğeri yoktur. Üst seviye olanları yaparken EEXISTS hatalarını göz ardı ettiğinizden emin olun.
("/tmp/",...)
, ("/tmp/a/",...)
, ("/tmp/a/b/",...)
,("/tmp/a/b/c/",...)
İşte kod örneğim (hem Windows hem de Linux için çalışıyor):
#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;
}
Kullanımı:
$ makePath 1/2 folderA/folderB/folderC
creating 1/2 ... OK
creating folderA/folderB/folderC ... OK
stat
(ile ilgili__STDC__
ön derleyici testine gerek olmadığı ) .
C ++ 17 dosya sistemi arayüzünden başlamanın standart kitaplığın bir parçası olduğu unutulmamalıdır. Bu, dizinler oluşturmak için aşağıdakilere sahip olunabileceği anlamına gelir:
#include <filesystem>
std::filesystem::create_directories("/a/b/c/d")
Daha fazla bilgi burada: https://en.cppreference.com/w/cpp/filesystem/create_directory
Ek olarak, gcc ile CFLAGS için "-std = c ++ 17" gerekir. Ve LDLIBS'ye "-lstdc ++ fs". İkincisi, gelecekte potansiyel olarak gerekli olmayacak.
Bu, öncekine benzer, ancak yinelemeli olarak geriye gitmek yerine dizge boyunca ileriye doğru çalışır. Son başarısızlık için errno değerini doğru değerle bırakır. Başta bir eğik çizgi varsa, döngünün dışında bir find_first_of () aracılığıyla veya öndeki / ve ön ayarını 1 olarak belirleyerek önlenebilecek fazladan bir döngü vardır. Verimlilik, bir İlk döngü veya bir ön döngü çağrısı ve döngü öncesi çağrıyı kullanırken karmaşıklık (biraz) daha yüksek olacaktır.
#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';
}
"C ++" dediniz ama buradaki herkes "Bash kabuğu" düşünüyor gibi görünüyor.
Gnu'nun kaynak kodunu kontrol edin mkdir
; daha sonra kabuk komutlarını C ++ 'da nasıl uygulayacağınızı görebilirsiniz.
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;
}
Bu yüzden mkdirp()
bugün ihtiyacım var ve bu sayfadaki çözümleri aşırı karmaşık buldum. Bu nedenle, bu konuya rastlayanlar için kolayca kopyalanabilen oldukça kısa bir pasaj yazdım, neden bu kadar çok satır koda ihtiyacımız olduğunu merak ediyorum.
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 != '\0') {
// Skip first character
p++;
// Find first slash or end
while(*p != '\0' && *p != '/') p++;
// Remember value from p
char v = *p;
// Write end of string at p
*p = '\0';
// Create folder from path to '\0' 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;
}
Const çevrimini ve dizeyi geçici olarak değiştirmeyi sevmiyorsanız, sadece a strdup()
ve free()
sonra yapın.
Bu gönderi "Dizin Ağacı Oluştur" için Google'da üst sıralarda yer aldığından, Windows için işe yarayacak bir yanıt göndereceğim - bu, UNICODE veya MBCS için derlenen Win32 API kullanılarak çalışacaktır. Bu, Mark'ın yukarıdaki kodundan alınmıştır.
Bu birlikte çalıştığımız Windows olduğundan, dizin ayırıcıları eğik çizgi değil, BACK-bölü işaretleridir. Bunun yerine düz eğik çizgi, değişiklik olurdu Eğer '\\'
için'/'
Şunlarla çalışacak:
c:\foo\bar\hello\world
ve
c:\foo\bar\hellp\world\
(yani: eğik çizgiye ihtiyaç duymaz, bu yüzden kontrol etmeniz gerekmez.)
"Yalnızca Windows'ta SHCreateDirectoryEx () kullanın" demeden önce , SHCreateDirectoryEx () ' in kullanımdan kaldırıldığını ve Windows'un gelecekteki sürümlerinden herhangi bir zamanda kaldırılabileceğini unutmayın .
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;
}
c:\this\is\a/mixed/path\of\slashes
Tipik olarak Windows eğik çizgileri ters eğik çizgilerdir. Olması gereken şey, arayan kişi yolu temizlemeli ve bu yöntemi çağırmadan önce tüm eğik çizgilerin uygun olduğundan emin olmalıdır.
Bunun eski bir soru olduğunu biliyorum, ancak Google arama sonuçlarında üst sıralarda görünüyor ve burada verilen cevaplar gerçekten C ++ dilinde değil veya biraz fazla karmaşık.
Lütfen benim örneğimde createDirTree () 'nin çok basit olduğunu unutmayın çünkü tüm ağır kaldırma (hata kontrolü, yol doğrulama) yine de createDir () tarafından yapılmalıdır. Ayrıca, dizin zaten varsa createDir () true döndürmelidir, aksi halde her şey çalışmaz.
Bunu C ++ 'da şu şekilde yapardım:
#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;
}
Elbette createDir () işlevi sisteme özgü olacak ve diğer yanıtlarda linux için nasıl yazılacağına dair yeterince örnek var, bu yüzden atlamaya karar verdim.
Burada pek çok yaklaşım açıklanmıştır, ancak bunların çoğunun kodunuza giden yolunuzun sabit kodlanması gerekir. Qt çerçevesinin iki sınıfı olan QDir ve QFileInfo kullanarak bu problem için kolay bir çözüm var. Zaten Linux ortamında olduğunuz için Qt'yi kullanmak kolay olmalıdır.
QString qStringFileName("path/to/the/file/that/dont/exist.txt");
QDir dir = QFileInfo(qStringFileName).dir();
if(!dir.exists()) {
dir.mkpath(dir.path());
}
Bu Yola yazma erişiminiz olduğundan emin olun.
mkdir -p /dir/to/the/file
touch /dir/to/the/file/thefile.ending
-p
seçenek aradığım şeydir. Teşekkürler!
dirname()
Dizin ağacını aşağıdan yukarıya hareket ettirmek için kullanan C / C ++ özyinelemeli işlevi burada . Mevcut bir atayı bulur bulmaz duracaktır.
#include <libgen.h>
#include <string.h>
int create_dir_tree_recursive(const char *path, const mode_t mode)
{
if (strcmp(path, "/") == 0) // No need of checking if we are at root.
return 0;
// Check whether this dir exists or not.
struct stat st;
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode))
{
// Check and create parent dir tree first.
char *path2 = strdup(path);
char *parent_dir_path = dirname(path2);
if (create_dir_tree_recursive(parent_dir_path, mode) == -1)
return -1;
// Create this dir.
if (mkdir(path, mode) == -1)
return -1;
}
return 0;
}
Diğerleri sana doğru cevabı verdi, ama yapabileceğin başka bir güzel şeyi daha göstereyim dedim:
mkdir -p /tmp/a/{b,c}/d
Aşağıdaki yolları oluşturacaktır:
/tmp/a/b/d
/tmp/a/c/d
Küme parantezleri, hiyerarşinin aynı düzeyinde aynı anda birden çok dizin oluşturmanıza izin verirken, -p
seçenek "gerektiğinde ana dizinler oluştur" anlamına gelir.