C++ open file for writing only if does not exists - c++

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)

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.

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

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.

Related

Safe use of std::tmpnam

so I am developing an application, which requires to create a file, write into it, call another program with that file is an input, and delete the file.
I looked for possible solution and one solution looks like this.
std::FILE* tmpf = std::tmpfile();
std::fputs("Hello, world", tmpf);
According to the documentation of std::tmpfile, if either the file is closed manually, or the program exits in natural way, the file will be deleted. This looks like good solution with one exception. It looks messy (using C I/O, instead of C++ streams).
Another solution would be to use std::tmpnam, which will generate unique file name.
std::string file_name = std::tmpname(NULL);
// (*)
std::fstream stream{file_name};
stream << "Hello World" << std::endl;
But there is a problem with this one too. If another program creates file with the same name, while my program is in (*), we will both be doing operations on same file, which is certainly something I'd like to avoid.
C++ STL (still) does not support file system operations, like checking if file exists. I could use something like stat to check that, but since checking for file and creating it is not atomic, it would not solve anything. And I did not find C++ STL method of atomic operation, which would: Check if file exists, if not, open it, if yes, fail.
So, my question is, what is the right way of solving this problem? Did I miss something?
Your specification "...create a file, write into it, call another program with that file is an input, and delete the file." is incompatible with tmpfile(). For one thing, there is no (portable) way to get the file name from the FILE pointer you get from tmpfile(), secondly on POSIX platforms tmpfile() will typically delete the file from the directory before returning from tmpfile() (if you're unfamiliar with POSIX filesystem semantics, the file exists as long as your process has an open file descriptor to it even after it's deleted from the directory, meaning there is no way to access it from the filesystem).
Given that, you'll have to use some kind of tmpname() type approach, and figure out a way to prevent two processes from simultaneously accessing it (file locks, check link count with stat(), or such).
Or better yet, don't use files for interprocess communication. For the simplest possible(?) case, create a pipe in the parent, connect it to stdin of the child.
Here is my almost not messy solution:
#include <cstdio>
#include <iostream>
#include <string>
#include <ext/stdio_filebuf.h> // libstdc++ specific
int main()
{
__gnu_cxx::stdio_filebuf<char> tmpfile_buf{std::tmpfile(), std::ios::in | std::ios::out | std::ios::binary};
std::iostream tmpstream{&tmpfile_buf};
// write in stream
tmpstream << "Hello World" << std::endl;
tmpstream.seekg(0);
// read from stream
std::string str;
std::getline(tmpstream, str);
std::cout << str << std::endl;
}
Live example
std::tmpfile() is used and a stream is build from a buffer having an underlying FILE*. This is a GNU extension and thus not portable :(.
I may have a Boost solution to suggest, but I can't test it here.
With a FILE* returned from std::tmpfile(), one can get a file descriptor:
#include <cstdio>
FILE* const tmpfile = std::tmpfile();
const int fd = ::fileno(tmpfile);
and at this stage, everything looks good to use boost::iostreams::file_descriptor_source:
#include <boost/iostreams/code_converter.hpp>
#include <boost/iostreams/maped_file.hpp>
file_descriptor_source tmpstream(fd, boost::iostreams::close_handle);

how a single file can be linked to two different streams at same time?

ifstream fin("test.txt");
ofstream fout ("test.txt");
The above 2 lines, if written in the same programme, does not produce any error or warning.
But how can we write and read into a same file at same time.
How does this works?
So, as comments say, the compiler as such doesn't REALLY know what you are trying to achieve (and there are certainly cases where you'd actually want to something similar to this, for example):
// With exceptions for file operations enabled:
try
{
ifstream fin("test.txt");
return true;
}
catch(...)
{
try
{
ofstream fout("test.txt");
}
catch(...)
{
cout << "Can't create file";
return false;
}
return true;
}
Now, that will (in a rather stupid and complicated way) check if the file exists or can be created. Would you like the compiler to complain about that too? The compiler would have to "understand" a lot of logic to follow when which file is used when, and see that both can't be used at the same (and yes, the compiler probably DOES understand that in this case, since we have a return immediately after the fin use - make the code a bit more complicated, and it won't).
Now, you can't read/write into the same file with the code:
ifstream fin("test.txt");
ofstream fout ("test.txt");
Depending on the OS (and the flags used in the layer below), you may be able to actually execute both of those lines without error - in Unix, files that are open remain open in the system, even if something else removes the file (which the second line will do), so you can read the "old" file, and write to a new one. That is of course not "read and write the same file at the same time", but reading one file and writing to another. And in most non-Unix filesystems, this will not work, since the OS will use the same directory entry for both calls, and open the existing file, then the second call will either make the file empty or fail (depending on what OS, and such).
The solution, if you really want to read and write the same file is to either:
use ios_base::in|ios_base::out as flags when you open the file.
use a temporary file for the output side, and rename the file when it's "done". Since most types of changes to text-files require that the file is "rewritten" [except for adding things to the end], this is often the preferred method. Then when the changes are done, rename the new file to the old one (with a remove of the original one first). This also means you always have at least one complete file (albeit with the wrong name) even if your program crashes.
Nothing in the C++ Standard precludes this - the request is just passed through to the Operating System, which may or may not allow it.
For example, on some OS/filesystems combinations if you go to create a file with the same name as an existing file, the OS hides the existing file's directory entry/entries so other apps can't open it, but existing processes that are using it will be able to continue to do so; when they all close the file will be deleted. Meanwhile, a new file can be created with the same name, and after that any apps opening the file will see whatever's been flushed to that new file.
On other systems, some kind of "in use" or "locked" error message may be generated, leading to a fail/bad state in the std::ofstream.
You should always test the success of file stream creation, e.g.:
if (std::ifstream in("filename.txt"))
...use in...
else
std::cerr << ... or throw std::runtime_error(...)
There is no reason how compiler can understand that you are doing something wrong, it will not remember all filenames you have ever used in your program. Moreover, if the filenames come from user input or simply from a variable, the compiler can never guess that they will be identical:
ifstream fin(s1);
ofstream fout (s2); // is s1==s2?
In fact, your case (hard-coded file name) is very unusual situation, so I do not think such a warning can be useful in a general case.
And, in addition to this all, this code can sometimes even work correctly.
For example, if test.txt is a named pipe, than you can safely open it simultaneously both for reading and writing. You will still have to do some workaround to prevent blocking, a simplest approach will be to open the pipe in different threads. But you can safely imagine an situation where named pipe operations will not block (I do not know whether this is possible in popular OSes by some OS option etc., but anyway you can always imagine such an OS), in such a case the following code should work without any problem:
// (for imaginary OS where named pipe operations do not block)
ifstream fin("test.fifo");
ofstream fout("test.fifo");
fout << 42;
int x;
fin >> x; // produces 42
So there is no reason for a compiler to warn or give an error. If you want to check in runtime whether the file open was successful, just check it.

C++ substitution of ios::noreplace

I'm using fstream to open a file for write. I don't want to overwrite an existing file so after some searching, I found ios::noreplace. But when I compile this:
#include <fstream>
using namespace std;
//......Did something else.
ofstream fout;
fout.open(outputFile,ios::noreplace);//outputFile is a C string
I get an error
error: ‘noreplace’ is not a member of ‘std::ios’
I'm just wondering is there any std:: subsitution for ios::noreplace?
Some searching on the internet reveals that you can add an existence check manually by attempting to open in "input" mode:
std::fstream myfile("thefile.txt", std::ios::in);
if (myfile)
{
// error, file exists!
}
else
{
myfile.close();
myfile.open("thefile.txt", std::ios::out); // OK now
}
noreplace never got into the standard. About four seconds of googling yields:
http://www.devx.com/tips/Tip/14544
In pre-standard C++, certain implementations of offered the flags ios::nocreate and ios::noreplace for controlling file creation. These flags were too platform-specific and never made it into the standard library, which supersedes the deprecated, pre-standard header. However, you can achieve the functionality of these obsolete flags rather easily.
fstream fs(fname, ios_base::in);// attempt open for read
if (!fs)
{
// file doesn't exist; create a new one
fs.open(fname, ios_base::out);
}
else //ok, file exists; close and reopen in write mode
{
// Should throw an error
}
The suggested answers are risky since they have race conditions.
Unless you can guarantee nobody will ever create that file while your are running this test, you should not use it.
As a workaround, use the non-portable method (on Linux for example open with O_CREAT|O_EXCL).
You can either use the resulting handle with code like boost to wrap it into an ofstream, or in this case use open() only to check and then create a new ofstream on the file (the latter assumes nobody deletes/renames the file in-between and thus might still have a race condition).
C++ not providing ANY safe way to create a file is a bad joke and likely the cause of quite a few security holes. You have to love standards that encourage bad practices by making writing correct code impossible.
The complaints are addressed! C++23 finally standardises the std::ios_base::noreplace flag to open a file for writing in exclusive mode, i.e. to fail if that file already exists.
Paper: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2467r1.html
Common standard library implementations are already supporting this in C++23 mode, including libstdc++ as bundled with GCC/g++.

Clearing Contents of a File in C++ knowing only the FILE *

Is it possible to clear the contents (ie. set EOF to the beginning/reset the file) in C++ knowing just the FILE*? I'm writing to a temp file with wb+ access and wish to sometimes clear it and truncate it without adding the calls to fclose and fopen. I dont think it's possible... but if not, why not?
Thanks in advance!
It will depend on your platform. The POSIX standard provides ftruncate(), which requires a file descriptor, not a FILE pointer, but it also provides fileno() to get the file descriptor from the FILE pointer.
The analogous facilities will be available in Windows environments - but under different names.
I don't believe this can be done using just the FILE*. You can always write null data through the end of the file but that won't truncate it.
Alternately if you have access to the filename (I can't tell from the question) you could use freopen which hides the close/open/truncate into a single function call.
#include <cstdio>
freopen(null, "w", filePtr);
see http://www.cplusplus.com/reference/clibrary/cstdio/freopen/ for more. espacialy the description for the parameter filename.

from file object to file name

I wonder if we can get the file name including its path from the file object that we have created for the file name in C and in C++ respectively
FILE *fp = fopen(filename, mode); // in C
ofstream out(filename); // in C++
ifstream in(filename); // in C++
Thanks!
You can't, in general. The file may not ever have had a file name, as it may be standard input, output, or error, or a socket. The file may have also been deleted; on Unix at least, you can still read to or write from a file that has been deleted, as the process retains a reference to it so the underlying file itself is not deleted until the reference count goes to zero. There may also be more than one name for a file; you can have multiple hard links to a single file.
If you want to retain the information about where a file came from, I would suggest creating your own struct or class that consists of a filename and the file pointer or stream.
There is no portable way to retrieve the file name of a FILE* object. It may not even be associated with an actual file (e.g. a FILE pointer for stdout).
There is no portable way. However particular platforms sometimes have ways to do that.
In Windows, if you can get the file's HANDLE (like the one you get from ::CreateFile() ), you can get the path from that using something like ZwQueryInformationFile().
From a FILE *, you can get a (Unix-style) file id using _fileno(). Then call _get_oshandle() to get the HANDLE.
Not sure how to do that from an std::ofstream, but you can research that.
Not sure how to do that on other OSes but it may be possible.