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.
Related
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.
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.
Whenever I encounter a substitute character http://en.wikipedia.org/wiki/Substitute_character while reading a file in C++ using getline(), it is interpreted as a EOF so I cannot advance with my reading in order to get the file entire content. So my question is, how can I skip the substitute characters and read the content of the file until the "real" EOF?
Open the file in binary mode instead of text mode. If you're using fopen, make open it in one of the "b" modes, e.g. "rb". If you're using a C++ ifstream object, open it with the ios::binary flag.
For example:
// C method
FILE *f = fopen("filename", "rb");
// C++ method
std::ifstream f("filename", std::ios::in | std::ios::binary);
I'm writing a method to replace a specified string from a binary file and it writes NULLs before the position I set with seekp, then writes the string and closes the stream. I only want to replace some bytes in the file. Before this piece of code I tried out with ofstream with ios::binary and ios::out flags. What's wrong to destroy all data in the file?
Before this piece of code, I open the file with an instance of ifstream to read the same position verifyng the first byte in the string. I only comment this for information.
Thank you all!
The code:
fstream ofs();
ofs.open(nomArchBin,ios::in | ios::out | ios::binary);
if (!ofs.good()) {
cout << "...";
return;
}
ofs.seekp(despEnArchivo,ios::beg);
char* registroChar = registroACadena(reg);
ofs.write(registroChar,cabecera.tamanioReg);
I know this sounds silly, but the only way to open a file for writing
and not to truncate it is to open it for reading as well: if you're
really doing ios::in | ios::out | ios::binary, it should work. (But
since you obviously reentered the code, and didn't copy/paste it, I'm
not sure if this is really what you did.)
Other points you have to pay attention to when trying to seek:
The file must be open in binary mode, and imbued with the "C"
locale. (IMHO, a file opened in binary mode should ignore the locale,
but this isn't what the standard says.)
Both `seekg` and `seekp` have the same effect; using either changes the
position of the other.
The only function which allows seeking to an arbitrary location is
the two argument seek; the one argument form can only be used to seek to
a position previously obtained by a tell.
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]