c++ std::fstream behaviour on MacOS - c++

On MacOS with gcc4.2 should the following code create a new file if none exists?
#include <fstream>
void test () {
std::fstream file ("myfile.txt", std::ios::in | std::ios::out);
}
By my logic it should, either open up an existing file for read/writing or create a new empty file for read/writing. But the behaviour I get is that it will not create a new file if 'myfile.txt' does not exist.
How do I get the same behavior as fopen("myfile.txt", "r+"); ?
Furthermore,
#include <fstream>
void test () {
std::ofstream file ("myfile.txt", std::ios::in | std::ios::out);
}
Will always truncate an existing file...
Is this the standard behavior?

First of all, I have no idea why you think that fopen("r+") creates a file if it doesn't exist - according to ISO C & C++, it does not, it just opens an existing file for read/write. If you want to create a file with fopen, you use "w+".
For streams, you just specify trunc:
std::ofstream file ("myfile.txt",
std::ios::in | std::ios::out | std::ios::trunc);
However, both this and fopen("w+") will truncate the file. There's no standard way to open the file without truncating if it exists, but create it if it does not exist in a single call. At best you can try to open, check for failure, and then try to create/truncate; but this may lead to a race condition if file is created by another process after the check but before truncation.
In POSIX, you can use open with O_CREAT and without O_TRUNC.

For the first case, you have specified that you are BOTH reading from and writing to the file. Hence, it will fail, if the file does not already exist. You could try to use two separate streams (one for reading and the other for writing), and construct the output stream, first. As for the second case, you can use "std::ios::app" (open in append mode) to prevent truncation. [Tested with Mac OS X 10.4.11, GCC 4.0.1]

Related

Can't access the file(Internal File Buffer NULL)

I have this strange bug. I have a program which writes text to the file using the fstream, but the file is not being created and therefore no text is appended. When I debug my code, it shows me this:
create_new_file = {_Filebuffer={_Pcvt=0x0000000000000000 <NULL> _Mychar=0 '\0' _Wrotesome=false ...} }.
But whenever I use ofstream everything works.
Here is the code:
std::fstream create_new_file{ fileName.str()};
std::unique_ptr<std::string> changes = std::make_unique<std::string>("");
std::cin >> *changes;
create_new_file << *changes << "\n";
Here is the code which works:
std::ofstream create_new_file{ fileName.str()};
I have seen a similar post on Stack Overflow but the answer did not resolve my issue. I have tried adding the std::ios::trunc to the fstream but that did not help. But whenever I use ofstream everything works just as expected.
The problem is that for bidirectional file streams the trunc flag must always be explicitly specified, i.e., if you want the file content to be discarded then you must write in | out | trunc as the second argument as shown below.
Thus, to solve the problem change std::fstream create_new_file{ fileName.str()}; to :
//-------------------------------------------------------------------------vvvvvvvvvvvvvvv---->explicitly use trunc
std::fstream create_new_file{ "output.txt", ios_base::in | ios_base::out | ios_base::trunc};
Working demo
This file stream buffer open reference is useful. It shows a table with the different modes and what happens when they are used.
When you open a std::fstream the default mode for the constructor is in | out. If we look that up in the table we see that this will fail if the file doesn't exist.
And you never check for failure (which you always should do).
If you only want to write to the file then use std::ofstream as it will open the files in out mode, which creates the file if it doesn't exist.
If you want to only append to the file, still use std::ofstream but use the mode out | app, which will create the file and make sure all output is appended (written to the end).

Read/write file only if it exists using fstream

I'm handling a file using fstream, and I need to read and write to it. However, even using std::ios:in, the file continues to be created if it does not exist:
std::fstream file("myfile.txt", std::ios::in | std::ios::out | std::ios::app);
Any thoughts?
Thanks in advance!
Read documentation carefully:
std::basic_filebuf<CharT,Traits>::open - cppreference.com
The file is opened as if by calling std::fopen with the second argument (mode) determined as follows:
mode
openmode & ~ate
Action if file already exists
Action if file does not exist
"r"
in
Read from start
Failure to open
"w"
out, out|trunc
Destroy contents
Create new
"a"
app, out|app
Append to file
Create new
"r+"
out|in
Read from start
Error
"w+"
out|in|trunc
Destroy contents
Create new
"a+"
out|in|app, in|app
Write to end
Create new
"rb"
binary|in
Read from start
Failure to open
"wb"
binary|out, binary|out|trunc
Destroy contents
Create new
"ab"
binary|app, binary|out|app
Write to end
Create new
"r+b"
binary|out|in
Read from start
Error
"w+b"
binary|out|in|trunc
Destroy contents
Create new
"a+b"
binary|out|in|app, binary|in|app
Write to end
Create new
This should explain everything.
Just drop app flag and you done.
https://wandbox.org/permlink/p4vLC8ane9Ndh1gN
There are different approaches, you can do it the old way
std::fstream file("myfile.txt", std::ios::in | std::ios::out); // edited after comment
if (!file)
{
// Can't open file for some reason
}
Or you can use standard library std::filesystem::exists introduced in C++17. Read more about it.
if (std::filesystem::exists("myfile.txt")) { // Exists }
Remember that file can exist but you can fail to interact with it (read/write from/to it).
If needed:
g++ -std=c++17 yourFile.cpp -o output_executable_name

File cleared out after reopening it with std::fstream

I want to open a file for reading, close it, and then open it again for writing using the same std::fstream. But when I reopen the file with writing permissions all the data inside is being cleared and it becomes 0 bytes long.
Here's my code:
char* data = new char[5];
std::fstream fs("myFile", std::ios::binary | std::ios::in);//opening with read
fs.read(data, 5);
fs.clear();
fs.close();
//Do some stuff
fs.open("myFile", std::ios::binary | std::ios::out);//opening with write
//The file is already empty at this point
When opening a file with the default write flag, i.e., std::ios::out with the <iostream> facility or "w" with the <cstdio> functions, there is a combination of POSIX flags happening behind your back - a truncate flag is added. This means that upon opening in write mode, the file content is discarded. To circumvent this, open the file the second time with
fs.open("myFile", std::ios::binary | std::ios::app);
The append mode moves the file cursor to the end of the file at each write operation and is not combined with the truncate flag, see here. Note that when you want to write to arbitrary positions in the existing file, you need to open it with
fs.open("myFile", std::ios::binary | std::ios::in | std::ios::out);
which does not truncate its content and allows for cursor positioning with the seek* functions.

How can I open a file for reading & writing, creating it if it does not exist, without truncating it?

What is the proper set of I/O flags for a std::fstream, where I want to be able to read from and write to the file, without truncating the file if it exists, but creating it if it does not?
I've tried
std::ios::binary | std::ios::in | std::ios::out
std::ios::binary | std::ios::in | std::ios::out | std::ios::ate
but neither of these create the file if it does not already exist.
I don't want std::ios::app, because I also need to be able to seek around the file at will, with both the get and put cursors.
One workaround, I suppose, would be to instantiate an std::ofstream first, then immediately close it and open the stream I really want, but that seems messy if it can be avoided with a single stream object.
At this time, I'm concluding that std::ios::in outright prevents this, and that I must use the workaround.
So:
if (!std::ostream(path.c_str()))
throw std::runtime_error("Could not create/open file");
std::fstream fs(path.c_str(), std::ios::binary | std::ios::in | std::ios::out);
if (!fs)
throw std::runtime_error("Could not open file");
// ... use `fs`
An investigation, from a Linux perspective (though much of this likely applies to other Unices):
At the syscall layer, you want open(O_RDWR | O_CREAT, 0666) (but not O_TRUNC or O_APPEND or a bunch of other flags, though arguably all files should be opened with O_CLOEXEC | O_LARGEFILE, but that's beside the point)
At the libc layer, there is no standard mode string that implies O_CREAT without O_TRUNC. However, you could use open followed by fdopen.
At the C++ library level, there is no standard way to pass the desired flags. However, using implementation-specific classes/functions or third-party libraries, it is possible; see How to construct a c++ fstream from a POSIX file descriptor?
Personally, I tend to do all I/O at the C or even syscall level, since the API is a lot nicer and it's more predictable. For input/output of class instances, I have my own templates.
Taking std::ios::binary as read, the remaining openmode probably you require is:
std::ios::in | std::ios::app
It's effect is as if to open the file with:
std::fopen(filename,"a+")
and the effect of that is:
open or, if it does not exist, create the file for reading and writing
write data at the end of the file.
If you open a file as an std::fstream with this openmode, it is not truncated if it exists. You may
read from the file wherever the fstream's tellg()\tellp() pointer points,
provided there is something there to read, and you can position that pointer
with the stream's seekg()\seekp() for reading. However, all writes will be
appended to the end of the file.
This openmode will therefore fit your bill unless you need to perform writes
into existing data.

ifstream not creating and opening a file for output

I am developing in C++ using NetBeans 6.9 on Ubuntu 11.04. I have declared the input and output file name strings and file streams thus
ifstream fpInputFile, fpOutputFile;
string inputFileName="", outputFileName="";
The input file name is assigned the name of an existing file as an input argument to the application. The output file name is given the same as the input name except that "_output" is inserted before the final period. So the output is written to the same directory as the input is located. Also I start netbeans with
su netbeans
so the IDE has root privileges to the directory. I try to open the files, and check whether they are opened thus.
fpInputFile.open(inputFileName.c_str(), ifstream::in);
fpOutputFile.open(outputFileName.c_str(), ifstream::out);
if (!(fpInputFile.is_open())) throw ERROR_OPENING_FILE;
if (!(fpOutputFile.is_open())) throw ERROR_OPENING_FILE;
The input file opens successfully but the output file does not.
Any help in determining why the output file is not opening for writing would be most appreciated.
Declare the output file as an ofstream rather than a ifstream.
You could also use a fstream for both input and output files.
The obvious problem is that you probably meant to open the file using a std::ofstream rather than an std::ifstream. This helps with actually writing to the stream although there are ways to write to an std::ifstream as long as it is opened for reading. For example, you could use the std::streambuf interface directly or use the std::streambuf to construct and std::ostream.
The more interesting question is: why isn't the file opened for writing when std::ios_base::in | std::ios_base::out is used for the open mode? std::ifstream automatically adds std::ios_base::in. It turns out, that the mode std::ios_base::in | std::ios_base::out doesn't create a file but it would successfully open an existing file. If you really want use an std::ifstream to open a file for output which potentially doesn't exist you would need to use either std::ios_base::out | std::ios_base::trunc or std::ios_base::out | std::ios_base::app:
the former would force the file to be created or truncated if it exists
the latter would force writes to append to the file in all cases
My personal guess is, however, that you are best off just using std::ofstream or, if you want to open the file for both reading and writing std::fstream (which, however, would also need to have std::ios_base::trunc or std::ios_base::app added to create a file if none exists).