3

I would like to open a file for writing with the standard library, but the file open should fail if the file already exists.

From what I can read in the documentation, ofstream::open only allows appending or truncating.

I could of course try to open for reading to check if the file exists, and reopen for writing if it doesn't, but there is no guarantee that the file will not be created by another process inbetween.

Could someone confirm this is not possible in C++ with the standard library (std::iostream) or with the C functions (FILE* functions)

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
galinette
  • 8,896
  • 2
  • 36
  • 87
  • Try opening the file for reading to check if it already exists. – πάντα ῥεῖ Jun 08 '16 at 17:46
  • @Olaf That's too much nit-picking –  Jun 08 '16 at 17:46
  • 1
    @DieterLücking: No! The typical functions for opening and handling a file differ in both languages. Worse, as OP does not show any code how exactly she opens the file. Both languages have very different standard libraries. – too honest for this site Jun 08 '16 at 17:48
  • There is no "atomic" file access whatsoever in C/C++. You have to solve it on OS-level. –  Jun 08 '16 at 17:49
  • ... `tmpfile` might be an exemption. –  Jun 08 '16 at 17:56
  • 1
    @πάνταῥεῖ : You did not read the full post! Someone could create the file between the check and the open for writing – galinette Jun 08 '16 at 19:07
  • @DieterLücking : what I mean by "atomic" is that if you open the file for reading in order to check if it exists, then close it, then open the file for writing, something may have happened inbetween, such as a file creation – galinette Jun 08 '16 at 19:08
  • @galinette You stole the quote "atomic" in your edit of the question. –  Jun 08 '16 at 19:12
  • @DieterLücking : yes, because your comment convinced me that the word "atomic" was misleading and unnecessary – galinette Jun 08 '16 at 19:17

3 Answers3

8

Since C11 (and thus also in C++17), for fopen you can use mode "x" — exclusive mode, see this:

File access mode flag "x" can optionally be appended to "w" or "w+" specifiers. This flag forces the function to fail if the file exists, instead of overwriting it.

Ruslan
  • 18,162
  • 8
  • 67
  • 136
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • 1
    Thanks! It's a pity that no equivalent was added to ofstream, but at least I have a standard way – galinette Jun 08 '16 at 19:19
  • 2
    This is misleading. The question is about C++, and this access mode is only available in C11. – Ruslan May 12 '17 at 16:22
  • @Ruslan - read this part: "or with the C functions (FILE* functions)". I added missing [c] tag – PiotrNycz May 12 '17 at 20:39
  • I was supposing that by the C functions the OP meant the C compatibility in C++ (like `` header and the like), not separate C language functions. But your view is also valid indeed. If you edit your answer to mention that this mode isn't available until C++17 (where C11 is referenced instead of C99), I'll be able to remove the downvote. – Ruslan May 12 '17 at 20:42
  • @Ruslan - fill free to edit "my" answer. All posts in SO are owned by SO community. If you think that this is necessary then why not - however I am not 100% convinced - for me your valuable comments under the answer are enough information for those who love such details. – PiotrNycz May 13 '17 at 12:59
3

There are no fstream ways of doing this, but std::fopen is as much C++ as std::sin.

If you absolutely must have an fstream object of this file and you need the atomic check, you should first call fopen then on success, fclose and fstream::open:

std::ofstream create_new_file_for_writing()
{
    FILE* fp = nullptr;
    std::string fname;
    do {
        fname = random_file_name(); 
        fp = fopen(fname.c_str(), "wx");
    } while(!fp);
    // here the file is created and you "own" the filename
    fclose(fp);
    return std::ostream(fname);
}
Borislav Stanimirov
  • 1,609
  • 12
  • 23
  • If I'm not mistaken, this code still has the race condition. You should replace the last two lines by `std::ostream mystream(fname); fclose(fp); return mystream` to prevent a new file to be created between the fclose and the stream creation. Although I'm not sure it is actually illegal to do that. Still with clang on linux my little test succeeded. – Eike Jun 16 '21 at 12:29
  • by the time fclose is called, the file is already created. There is no race condition – Borislav Stanimirov Jun 28 '21 at 13:48
  • Oh, of course, thanks! I was (wrongly) under the impression, that fopen holds something like a lock on the file and only really creates the file if something is written. – Eike Jun 29 '21 at 09:46
2

In std::ofstream by itself, no. Opening a file for writing always creates a new file if it does not already exist. There is no option to change that behavior. Opening a file for reading fails if the file does not exist.

However, on Windows at least, the Win32 API CreateFile() function has a CREATE_NEW flag that fails to open the file if it already exists. On other platforms, there may be flags available for _fsopen() and fopen() do accomplish the same thing.

It is possible to attach a FILE* to a std::ofstream (or maybe this is just a Microsoft extension, I am not sure), and in Visual C++ a FILE* can be created for a HANDLE returned by CreateFile() by using _open_osfhandle() with _fdopen(). See this question for examples:

Can I use CreateFile, but force the handle into a std::ofstream?

Other compilers/platforms may provide similar extensions for initializing an std::ofstream, you will have to look around.

Community
  • 1
  • 1
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770