Reading and Writing to the Same File Using the Same Fstream

Reading and writing to the same file using the same fstream

You are falling foul of a restriction upon the intermixing of read and
write operations on a file opened in update mode that MS's fstream
library inherits from from the its C <stdio.h> implementation.

The C Standard (I cite C99, but it doesn't differ in this point from C89)
at 7.19.5.3/6 states:

When a file is opened with update mode ('+' as the second or third character in the
above list of mode argument values), both input and output may be performed on the
associated stream. However, output shall not be directly followed by input without an
intervening call to the fflush function or to a file positioning function (fseek,
fsetpos, or rewind), and input shall not be directly followed by output without an
intervening call to a file positioning function
, unless the input operation encounters end-
of-file.

(my emphasis).

So your stream.seekp(1) solution, which devolves to a C fseek, is correct.

The GNU C library does not have this Standard limitation, so your code as posted works
as expected when built with GCC.

The MS <fstream> library is compliant with the C++ Standard in inheriting
this restriction. fstreams are implemented using basic_filebuf<charT,traits>. In the (C++11) Standard's account of this template, at § 27.9.1.1/2, it simply says:

The restrictions on reading and writing a sequence controlled by an object of class basic_filebuf are the same as for reading and writing with the Standard C library FILEs.

reading and writing to the same file c++

The problem is the wrong usage of seekp():

  • you use it with one single parameter ios::end in m_haystackMapfile.seekp(std::ios::end)
  • but single parameter only works for absolute positionning. So ios::end is converted into an integer and will locate you at an unexpected place (on my implementation it's 2).
  • you have to use m_haystackMapfile.seekp(0, std::ios::end) instead

There is another problem: the istream_iterator<>() that you use in your std::copy() will read the stream until it reaches its end. So the failbit and eofbit will be set.

Connsequently, no stream operation will succed until you clear the flags: m_haystackMapfile.clear();

Reading and writing to the same file using fstream

When switching between input and output for a filestream without an intervening seek, you get undefined behavior. It doesn't matter where you seek to but you need to seek! For example, you can seek to zero characters away from the current position or, more likely, back to the position where the value actually started:

std::streampos start = input.seekg(0, std::ios_base::cur);
if (input >> balance) {
input.seekp(start);
input << (balance - amount);
}

Note, however, that the stream won't make space for additional characters, i.e., if what you read is shorter than what you write, you'll overwrite data following the originla input. Likewise, you will only overwrite the characters you overwrite. I'd recommened against doing anything like that. If you want to update a file, make sure you are using fixed width records!

Of course, you also shoudn't use input.eof() to verify if the stream is any good: if the stream goes into failure mode, e.g., due to a misformatted input, you'll never reach the point where input.eof() yields true, i.e., you'd get an infinite loop. Just use the stream itself as condition. Personally, I would use something like

while (input >> balance && Balindex != index) {
++Balindex;
}

use fstream to read and write in the same time

Look at this answer: https://stackoverflow.com/a/17567454/11829247 it explains the error you are experiencing.

Short version: Input and output is buffered and interleaving reads and writes only work if you force buffer updates in between.

This works for me:

#include <iostream>
#include <fstream>
#include <string>

int main()
{
std::fstream ioFile;
char ch;
ioFile.open("search.txt", std::ios::in | std::ios::out);
if (!ioFile)
{
std::cout << "problem opening the file";
return 1;
}

while (ioFile >> ch)
{
if (ch == 'z')
{
ioFile.seekp(-1, std::ios_base::cur);
ioFile << "x";
ioFile.flush();
}
}

ioFile.close();
return 0;
}

The difference is that I use ioFile.seekp(-1, std::ios_base::cur); to move one step back from the current position. You could also use ioFile.seekp((int)ioFile.tellg() -1); - note the -1.

Then after stepping back and overwriting the z, use ioFile.flush(); to force the write to be pushed to file. This also means that the read buffer is updated, without this the read operation just steps back in its buffer and keeps reading the same buffered z.

Reading and writing to the same file fstream

You have a few problems here.

First the command json json_data(fs); reads to the end of the file setting the EOF flag. The stream will stop working until that flag is cleared.

Second the file pointer is at the end of the file. If you want to overwrite the file you need to move back to the beginning again:

if (fs.is_open())
{
json json_data(fs); // reads to end of file
fs.clear(); // clear flag
fs.seekg(0); // move to beginning

Unfortunately that still doesn't fix everything because if the file you write back is smaller than the one you read in there will be some of the old data tagged to the end of the new data:

    std::cout << "Operation successfully performed\n";
std::cout << json_data.at("Number") << std::endl;
std::cout << json_data.at("Test") << std::endl;
std::cout << json_data.at("Foo") << std::endl;

json_data.at("Foo") = 4.32; // what if new data is smaller?

Json file:

{
"Foo": 4.32, // this number is smaller than before
"Number": 100,
"Test": "Test1"
}} // whoops trailing character from previous data!!

In this situation I would simply open one file for reading then another for writing, its much less error prone and expresses the intention to overwrite everything.

Something like:

#include "json.hpp"
#include <iostream>
#include <fstream>
#include <string>

using json = nlohmann::json;

void readAndWriteDataToFile(std::string fileName) {

json json_data;

// restrict scope of file object (auto-closing raii)
if(auto fs = std::ifstream(fileName))
{
json_data = json::parse(fs);

std::cout << "Operation successfully performed\n";
std::cout << json_data.at("Number") << std::endl;
std::cout << json_data.at("Test") << std::endl;
std::cout << json_data.at("Foo") << std::endl;
}
else
{
throw std::runtime_error(std::strerror(errno));
}

json_data.at("Foo") = 4.32;
std::cout << json_data.at("Foo") << std::endl;
std::string json_content = json_data.dump(3);

if(auto fs = std::ofstream(fileName))
{
fs.write(json_content.data(), json_content.size());
std::cout << "Done" << std::endl;
}
else
{
throw std::runtime_error(std::strerror(errno));
}

}

int main()
{
try
{
std::string fileName = "C:/new/json1.json";
readAndWriteDataToFile(fileName);
}
catch(std::exception const& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

How to read and write in file with `fstream` simultaneously in c++?

Nawaz' comment is correct. Your read loop iterates until the fstream::operator bool (of ofile) returns false. Therefore, after the loop, either failbit or badbit must have been set. failbit is set when loop tries to read for the final time but only EOF is left to read. This is completely OK, but you must reset the error state flag before trying to use the stream again.

// ...
ofile.clear();
ofile << "stackexchnange" << endl;

Should I read and write from the same file?

Yes, std::fstream supports both reading and writing using the same file stream object. Since you initially didn't show your code, I could only guess that you are not setting the file position pointer properly. When using seekg() or seekp(), keep in mind that in case of file streams, these both refer to the same position pointer. (For std::stringstream, these are different, but not for std::fstream.)

To create a file (as opposed to just opening an existing one), you need to specify the trunc mode. Also, you need both in and out modes (for reading and writing). And since you are dealing with binary data (not text), open the file in binary mode. In other words, do f.open("hello.txt", std::ios_base::binary | std::ios_base::trunc | std::ios_base::in | std::ios_base::out);.

Also, when printing the contents of the file, you need to cast b[i] to an integer, because the << operator overload used in std::cout for a char argument will try to print a character, not its numerical value. In other words, do std::cout << static_cast<int>(b[i]) << '\n';.

Reading and writing the same file simultaneosly with c++

It is not clear from your code [1] and problem description what is in the file and why you expect "000/123/FH/", but I can state that the getline function is a buffered input, and you don't have code to access the buffer. In general, it is not recommended to use buffered and unbuffered i/o together because it requires deep knowledge of the buffer mechanism and then relies on that mechanism not to change as libraries are upgraded.

You appear to want to do byte or character[2] level manipulation. For small files, you should read the entire file into memory, manipulate it, and then overwrite the original, requiring an open, read, close, open, write, close sequence. For large files you will need to use fread and/or some of the other lower level C library functions.

The best way to do this, since you are using C++, is to create your own class that handles reading up to and including a line separator [3] into one of the off-the-shelf circular buffers (that use malloc or a plug-in allocator as in the case of STL-like containers) or a circular buffer you develop as a template over a statically allocated array of bytes (if you want high speed an low resource utilization). The size will need to be at least as large as the longest line in the later case. [4]

Either way, you would want to add to the class to open the file in binary mode and expose the desired methods to do the line level manipulations to an arbitrary line. Some say (and I personally agree) that taking advantage of Bjarne Stroustrup's class encapsulation in C++ is that classes are easier to test carefully. Such a line manipulation class would encapsulate the random access C functions and unbuffered i/o and leave open the opportunity to maximize speed, while allowing for plug-and-play usage in systems and applications.

Notes

[1] The seeking of the current position is just testing the functions and does not yet, in the current state of the code, re-position the current file pointer.

[2] Note that there is a difference between character and byte level manipulations in today's computing environment where utf-8 or some other unicode standard is now more common than ASCII in many domains, especially that of the web.

[3] Note that line separators are dependent on the operating system, its version, and sometimes settings.

[4] The advantage of circular buffers in terms of speed is that you can read more than one line using fread at a time and use fast iteration to find the next end of line.

C++ Reading and Writing (same NAMED file)

If you want to read and write from one file then you can use fopen() from stdio.h which would look like this:

#include <stdio.h>
#include <iostream>

FILE *file;

int main()
{
file = fopen("myfile.txt", "w+");
fputs("test", file);
std::cout << "Writing test to myfile.txt" << std::endl;
fflush(file);
rewind(file);
char *myString = new char[5];
fread(myString, 1, 5 * sizeof(char), file);
std::cout << "Reading " << myString << " from myfile.txt" << std::endl;
}

You can read more about these functions here: https://www.cplusplus.com/reference/cstdio/



Related Topics



Leave a reply



Submit