23

I'm using fopen("aaa\bbb\ccc\myfile.txt","w") to create output file. But folders aaa, bbb and ccc don't exist. What is the best way to solve this problem? Is there are any standard way to ask fopen() to create all needed folders?

Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
vico
  • 17,051
  • 45
  • 159
  • 315
  • 9
    No, `fopen` will never make directories. There's a [question with good answers already](http://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix/11425692) about an extended `mkdir()` that makes multiple levels, which is similar to what you want. You'd need to do that multi-level mkdir on `"aaa/bbb/ccc/"` then call `fopen`. –  Jan 20 '14 at 14:35
  • 2
    For windows you could use information in this post http://stackoverflow.com/questions/2834737/creating-new-folders-if-they-dont-exist-for-fopen – Alex Jones Jan 20 '14 at 16:12
  • 3
    Remember to double those backslashes, in general that is. Won't help with the problem since `fopen()` simply doesn't support creating directories, but *if* the dirs existed it would be necessary. – unwind Jan 20 '14 at 16:17
  • @WumpusQ.Wumbley Why not answer instead of make a comment? So the author would be able to close this question. – Jérôme Pouiller Mar 23 '18 at 16:45
  • Folders are a GUI artefact. What the file system deals with is [directories](https://en.wikipedia.org/wiki/Directory_(computing)) and they are operating system specific. Standard C11 does not know about directories – Basile Starynkevitch May 27 '18 at 06:56

7 Answers7

1

As others have noted, there is no cross-platform support for directories in the C standard library.

As far as my understanding goes, fopen() and friends don't even know or care that the /-es or \-es in the path you give them are used as directory separators in the two major operating system families in use today.

For a cross-platform solution for single directory creation, see @dbush 's answer.

You can then wrap this with a function of your own that will take the complete file/directory path including all parent folders and create all the required parent folders (if needed), one-by-one.

For this, you will need to devise a way of extracting out each successive parent directory from your file path, starting from the top-most/left-most directory. Take care with splitting strings in C, as functions like strtok() can be unsafe —you might be better off with a different approach for converting the path into a list of directories to create.

You may also need to handle cases where directories already exist, or where a file exists at a path where you wanted to place one of your directories.

saxbophone
  • 779
  • 1
  • 6
  • 22
1

Here is another alternative written in plain C, and that does not rely on strtok (which mangles the passed file path string):

const char pathSeparator =
#ifdef _WIN32
  '\\';
#else
  '/';
#endif

// Given a file path, create all constituent directories if missing
void create_file_path_dirs(char *file_path) {
  char *dir_path = (char *) malloc(strlen(file_path) + 1);
  char *next_sep = strchr(file_path, pathSeparator);
  while (next_sep != NULL) {
    int dir_path_len = next_sep - file_path;
    memcpy(dir_path, file_path, dir_path_len);
    dir_path[dir_path_len] = '\0';
    mkdir(dir_path, S_IRWXU|S_IRWXG|S_IROTH);
    next_sep = strchr(next_sep + 1, pathSeparator);
  }
  free(dir_path);
}
0

If you can mix some C++ in your program, boost.filesystem library has a very handy function to create all intermediate directories.

bool create_directories(const path& p);

Tanveer Badar
  • 5,438
  • 2
  • 27
  • 32
0

The C Std. Lib. functions for opening and writing-to files, while they can create new files in existing directories, they cannot create new directories. To do that, I suggest using the "mkdir" function from header "sys/stat.h" and the "chdir" function from header "unistd.h".

For example, if writing a C program to be used on Cygwin on Windows 10, something like this would work:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <error.h>
int main (void)
{
   FILE * fp;
   chdir("/cygdrive/d");
   if (mkdir("aaa", S_IRWXU|S_IRWXG|S_IROTH))
   {
      error(EXIT_FAILURE, errno, "Failed to create directory aaa");
   }
   chdir("aaa");
   if (mkdir("bbb", S_IRWXU|S_IRWXG|S_IROTH))
   {
      error(EXIT_FAILURE, errno, "Failed to create directory bbb");
   }
   chdir("bbb");
   if (mkdir("ccc", S_IRWXU|S_IRWXG|S_IROTH))
   {
      error(EXIT_FAILURE, errno, "Failed to create directory ccc");
   }
   chdir("ccc");
   fp=fopen("MyFile.txt", "w");
   if (!fp)
   {
      error(EXIT_FAILURE, errno, "Failed to open file for writing");
   }
   fprintf(fp, "Test.\n");
   fclose(fp);
   return 0;
}

That will create the directory "D:\aaa\bbb\ccc", then create a file in that directory called "MyFile.txt", then write "Test.\n" to that file.

0

Probably not as elegant as other ones but:

system("mkdir aaa\\bbb\\ccc\\");
FILE * fp;
fp = fopen("aaa/bbb/ccc/myfile.txt","w");

For linux use -p option and slashes.

backslashes should be in system() function if you're using Windows

Neuron
  • 5,141
  • 5
  • 38
  • 59
C Chorche
  • 39
  • 6
-1

if you are using fopen() then you should create the parent directories first.

you can simply do this by

system("mkdir -p aaa/bbb/ccc");
fopen("aaa/bbb/ccc/myfile.txt","w");

don't forget to include header file <stdlib.h> in this program

-3

fopen() is used to open input streams for text or binary files and not the directory files. If you want to have your file open in a path which holds non-existant directories, I'd suggest you use the mkdir() method of the /sys/stat.h header file. Once you have the directories ready you can used fopen() to open the file you need(or create it if not already present). This is the approach that I used to create a file in the a directory of my $HOME folder:-

char* home_dir= getenv("HOME"); 
/*creates the directory with specified modes*/
mkdir(strcat(home_dir, "/make-a-note/"),S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 
/*used the strcat() to generate the file-path.*/
personal_work=fopen(strcat(home_dir,"personal_work.txt"),"a+");
  • 1
    Don't use that code, the `strcat()` call is evil: It definitely overruns the buffer holding the environment variables content. This undefined behavior will likely trash some other environment variable(s), or worse. – cmaster - reinstate monica Mar 14 '17 at 17:12