C++ Binary File method is removing content from the file? - c++

I have an assignment where I am writing input on various things (in the form of structs) and then writing to a binary file. I have to be able to both read and write to the file while the program is open. One of the methods needs to print out all of the clients in the binary file. It seems to be working, except whenever I call that method, it seems to erase the contents of the file and prevent more from being written to it. Here's the applicable snippets:
fstream binaryFile;
binaryFile.open("HomeBuyer", ios::in | ios::app | ios::binary);
The same file is supposed to be usable between times you run the program, so I should open it with ios::app, correct?
Here's the method to add an entry:
void addClient(fstream &binaryFile) {
HomeBuyer newClient; //Struct the data is stored in
// -- Snip -- Just some input statements to get the client details //
binaryFile.seekp(0L, ios::end); //This should sent the write position to the
//end of the file, correct?
binaryFile.write(reinterpret_cast<char *>(&newClient), sizeof(newClient));
cout << "The records have been saved." << endl << endl;
}
And now the method to print all the entries:
void displayAllClients(fstream &binaryFile) {
HomeBuyer printAll;
binaryFile.seekg(0L, ios::beg);
binaryFile.read(reinterpret_cast<char *>(&printAll),sizeof(printAll));
while(!binaryFile.eof()) { //Print all the entries while not at end of file
if(!printAll.deleted) {
// -- Snip -- Just some code to output, this works fine //
}
//Read the next entry
binaryFile.read(reinterpret_cast<char *>(&printAll),sizeof(printAll));
}
cout << "That's all of them!" << endl << endl;
}
If I step through the program, I can input as many clients as I want, and it will output them all the first time I call displayAllClients(). But as soon as I call displayAllClients() once, it seems to clear out the binary file, and any further attempts at displaying clients gives me no results.
Am I using seekp and seekg incorrectly?
From what I understand, this should set my write position to the end of the file:
binaryFile.seekp(0L, ios::end);
And this should set my read position to the beginning:
binaryFile.seekg(0L, ios::beg);
Thanks!

Pasting comment in as this resolved the issue.
You need to call binaryFile.clear() before seekp() and seekg() if EOF is set, otherwise they won't work.

This is the documentation for ios::app
ios::app
All output operations are performed at the end of the file, appending the
content to the current content of the file. This flag can only be used in
streams open for output-only operations.
Since this is homework, I'll let you draw your own conclusions.

Related

std::ofstream properly seek the file and add element into it

I am trying to add elements into a .json file between [] as last.
How can I move the cursor to add elements between [...] with efficiently with std::ofstream?
I have tried several open modes but there are strange things. First I created this question about not able to use the file streaming for read and write because of the overwrite issue.
#include <iostream>
#include <fstream>
int main ()
{
char errmsg[2048];
std::ofstream ostream;
ostream.exceptions(std::ios_base::badbit);
try
{
ostream.open("LS22731.json", std::fstream::ate | std::fstream::in);
strerror_s(errmsg, 2048, errno);
std::cout << "Error (" << errno << "): " << errmsg << std::endl;
if (ostream && ostream.is_open())
{
auto ppos = ostream.tellp();
std::streampos sub = 1; //
std::cout << "Tellp: " << ppos << std::endl; // Always show zero but file has large data
if (ppos > 1)
ostream.seekp(ppos - sub) << "aa";
ppos = ostream.teelp();
std::cout << "New tellp: " << ppos << std::endl;
ostream.close();
}
}
catch (std::ios_base::failure& fb)
{
std::cout << "Failure: " << fb.what() << std::endl;
char errmsg[2048];
strerror_s(errmsg, 2048, errno);
std::cout << "Error (" << errno << "): " << errno << std::endl;
}
}
I searched about open modes then I found this but is it good to open file with both mode std::fstream::ate | std::fstream::in together for std::ofstream? And when I open the file with std::fstream::out mode it is rewriting so deleting whole document,
std::fstream::out: Delete all contents of the file (overwrite)
std::fstream::app: Cannot move the cursor with seekp
std::fstream::ate: Delete all contents of the file (overwrite)
std::fstream::binary: Delete all contents of the file (overwrite)
std::fstream::ate | std::fstream::app: Cannot move the cursor with seekp
std::fstream::ate | std::fstream::out: Delete all contents of the file (overwrite)
std::fstream::ate | std::fstream::in: Can move the cursor but not insert delete all after.
I don't want to use c FILE.
Well JSON files are err... sequential text files. That means that the file contains a stream of bytes representing the JSON content. And AFAIK, no filesystem has provision for inserting data in the middle of a sequential file. The foolproof way is:
copy up to the insertion point to a temp file
write the new data
add the remaining data from the original file
rename the old file to a backup name
rename the temp file with the original name
(optionaly) remove the backup file
The brave way is to move the second part up by chunks starting from the end to create an emply place to put the data write the new data in that place, and pray all along the operation for no problem in the middle because the file would be irremediably corrupted.
Those 2 ways can process files of arbitrary sizes. For small files, you could load everything in memory, write the new data at the insertion point and rewrite the remaining data after the new data. You just need to use a default fstream and use neither ate nor trunc. out does not mean deleting all the file content. You simply replace the original bytes at the place where you write.
So you should use:
ostream.open("LS22731.json", std::fstream::out | std::fstream::in);
Then you:
read up to your insertion point and discard the data
note the position with tellp
read the end of file and save it
go to the insertion point
write the new data
write the saved data
close the stream
Here is an adaptation of the previous algorithm. The cautious points as:
you must use a fstream with std::fstream::out | std::fstream::in mode to be able to read and write a file. The file must exist and you will be initially positioned at the beginning of the file
to reliably be able to compute positions, you must open the file in binary mode (std::fstream::binary)(should be possible in text mode but I could not find my way...)
Here is a close adaptation of your code: it opens the file, search for the first closing bracket (]), and inserts ,"h" before to simulate adding a value into a list.
...
std::fstream ostream;
ostream.exceptions(std::ios_base::badbit);
try
{
// use binary mode to ba able to relyably seek the file.
ostream.open("LS22731.json",
std::fstream::out | std::fstream::in | std::fstream::binary);
strerror_s(errmsg, 2048, errno);
std::cout << "Error (" << errno << "): " << errmsg << std::endl;
if (ostream && ostream.is_open())
{
std::streampos ppos;
// search the first ]
ostream.ignore(std::numeric_limits<std::streamsize>::max(), ']');
// we want to insert just before it
ppos = ostream.tellg() - std::streampos(1);
ostream.seekg(ppos); // prepare to read from the ]
std::string old = "", tmp;
// save end of file, starting at the ]
while (std::getline(ostream, tmp)) {
old += tmp + "\n";
}
ostream.clear(); // clear eof indicator
ostream.seekp(ppos, std::ios::beg); // go back to the insertion point
ostream << ",\"h\""; // add some data
ostream << old; // add the remaining of the original data
ostream.close();
}
...
Disclaimers:
DO NOT PRETEND I ADSISED YOU THIS WAY. If there is a problem in the middle of processing, the file will be irremediately corrupted.
it will fail miserabily if a text field contains a closing bracket, because it is not a JSON parser
If you open a file for reading, you cant set the write head of it.
You are using std::ofstream with ios::in mode which I'm not sure is effective. but std::ofstream must be opened with ios::out or ios::app. When you override the default you should give also the default.
If you need to open a file for both read and write, you should use std::fstream.
Another issue is that you trying to add some string in the middle of a text file, and it is not so good idea, it is not similar to paste some string in a text file when opened in Notepad. you must replace a section with another section with the same length, pushing some string won't move the rest of the data forward.
I think the easy way is to read the whole JSON to memory, process it by add or remove some data, and finally rewrite the whole JSON to the file.

unable to read and write using same fstream object in c++

I'm trying to use same fstream object for first write the file and after that read the file.
when I'm using below code then the codes of writing the file is working but I'm getting junk output instead of texts which written in the file.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main() {
fstream file;
file.open("test.txt",ios::in|ios::out| ios::trunc);
if (!file) {
cout << "Error";
}
else {
cout << "success";
file <<"\n\n1st Line\n 2nd line \n 3rd line\n";
string filecontent;
while (file.good()) {
getline(file, filecontent);
cout << filecontent << endl;
}
}
return 0;
}
Output
This code has two separate problems. The first (which others have already pointed out to at least some degree) is that your loop isn't detecting the end of the file correctly. In fact, almost any time you use while (!file.eof()) or while (file.good()), it's going to be a mistake--it won't detect end of file at the right time, so (for example) when you reach the end of the file, you won't detect it at the right time, and you'll see the last item in the file appear to be read twice before the loop exits.
In addition to that, however, you have a problem in that you're writing to the file, then immediately trying to read. That's simply not allowed--you want to do a seek any time you switch between reading and writing.
In this case, you have a bit of a further problem. Since you've just written data into the file, your file's current position is at the end of the file. So even if you could just start reading without seeking, you'd start reading from the end of the file. That, of course, would immediately fail.
So you also really need to seek back to the beginning of the file to be able to read it back in.
So, the big changes here are adding a line like: file.seekg(0); after you finish writing, but before you start to try to read that data back in, and then changing your reading loop to something like:
while (getline(file, filecontent)) {
cout << filecontent << endl;
}
One last point: although it's not going to make a big difference in this case, I'd advise using "\n" instead of std::endl. std::endl writes a new-line and flushes the file buffer. When you're writing to the screen it won't make any real difference, but when writing to a normal file flushing the buffer unnecessarily can and will slow your code substantially (10x slower is pretty common).

Why is stream::ignore not working as intended?

As far as I know, stream.ignore(n, 'n') should ignore an (n) amount of characters or if ā€˜\nā€™ is reached, and skip over to the next line, however, when I run the next code:
// include...
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
while (!stream.eof()) {
std::string a{};
// getline(stream, a); <--- Tried this, didn't work either
stream.ignore(99, '\n');
} // Skip to the last line without any number, in theory
std::cout << info << std::endl; // Check if the output it's correct (Which is)
stream << info; // Insert the info
stream.close(); // Close the file
}
void main() //Main
{
std::cout << "Enter your name, followed by the info you want to add to infoFile:" << std::endl;
std::string info, temp = "";
std::getline(std::cin, temp); // Get the info input
std::stringstream sstream;
sstream << temp;
sstream >> temp >> info; // Remove the name keeping only the info
temp = ""; // ^
std::string::size_type sz;
insertInfo(stoi(info, &sz)); // Convert info string into an integer and insert it in infoFile
}
The console prints out the "info" correct value, however, when I check info.txt, in which I previously wrote a '0' on, you don't see any change.
I tried removing the "ignore" function and it overwrites the 0, which is exactly what I was trying to prevent.
I also tried using "getline" function but the same thing happens.
What is the error here?
Problem
Cannot write to file.
Why
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
Opens file with default permissions, which includes reading. The C++ Standard says I should expect "r+" behaviour and the C Standard says a file opened with "r+" behaviour must exist in order to be read (Someone please add a link if you have one). You cannot create a new file. This is problem 1. The Asker has dealt with this problem by providing a file.
Note: take care when working with files via relative paths. The program's working directory may not be where you think it is. This is problem 1a. It appears that the Asker has this taken care of for the moment.
while (!stream.eof()) {
Common bug. For more details see Why is iostream::eof inside a loop condition considered wrong? In this case since all you're looking for is the end of the file, the fact that the file hasn't been opened at all or has encountered any read errors is missed. Since a file in an error state can never reach the end of the file this quickly becomes an infinite loop. This is problem 2.
std::string a{};
// getline(stream, a); <--- Tryied this, didn't work neither
stream.ignore(99, '\n');
Always test IO transactions for success. This call can fail unchecked.
} // Skip to the last line without any number, in theory
Assuming nothing has gone wrong, and since we're not checking the error state assuming's all we can do, the file has reached the end and is now in the EOF error state. We can't read from or write to the stream until we clear this error. This is problem number 3 and likely the problem the Asker is struggling with.
std::cout << info << std::endl; // Check if the output it's correct (Wich is)
stream << info; // Insert the info
This can fail unchecked.
stream.close(); // Close the file
This is not necessary. The file will be closed when it goes out of scope.
}
Solution
void insertInfo(int info) {
std::fstream stream("infoFile.txt"); // Open the file
while (!stream.eof()) {
stream.ignore(99, '\n');
} // Skip to the last line without any number, in theory
std::cout << info << std::endl; // Check if the output it's correct (Wich is)
stream.clear(); // Added a call to clear the error flags.
stream << info; // Insert the info
stream.close(); // Close the file
}
Now we can write to the file. But let's improve this shall we?
void insertInfo(int info) {
std::fstream stream("infoFile.txt");
while (stream.ignore(99, '\n')) // moved ignore here. now we ignore, then test the result
{
}
stream.clear();
stream << info << '\n'; // added a line ending. Without some delimiter the file
// turns into one big number
}
Note that this isn't exactly kosher. If any ignore fails for any reason, we bail out and possibly write over data because the code blindly clears and writes. I'm not spending much time here trying to patch this up because we can get really, really simple and solve the problem of creating a non-existent file at the same time.
void insertInfo(int info) {
std::fstream stream("infoFile.txt", std::ios::app);
stream << info << '\n';
}
Two lines and pretty much done. With app we append to the file. We do not need to find the end of the file, the stream automatically points at it. If the file does not exist, it is created.
Next improvement: Let people know if the write failed.
bool insertInfo(int info) {
std::fstream stream("infoFile.txt", std::ios::app);
return static_cast<bool>(stream << info << '\n');
}
If the file was not written for any reason, the function returns false and the caller can figure out what to do. The only thing left is to tighten up the stream. Since all we do is write to ti we don't need the permissiveness of a fstream. Always start with the most restrictive and move to the least. This helps prevent some potential errors by making them impossible.
bool insertInfo(int info) {
std::ofstream stream("infoFile.txt", std::ios::app);
return static_cast<bool>(stream << info << '\n');
}
Now we use an ofstream and eliminate all the extra overhead and risk brought in by the ability to read the stream when we don't read the stream.

ofstream keeps giving me the last line of the function instead of showng everything

so I have been trying to figure out a way to have a function enter whatever is written in a file and to output into a Sha-256 function. maybe why do I want to do that? Simple enough I am trying to just teach myself how things work in c++, however it displays the last line for the function ofstream. this is my main function
ifstream input ("/home/findme/Desktop/text.txt"); // opens text.txt file.
string s;
while (getline (input, s))
{
// loop to read every line one by one of the file.
string c = s; // enters function to be converted.
string output1 = sha256(c); // gives back the value of the string.
cout << "sha256('"<< c << "'):" << output1 << endl; //displays the results.
ofstream outfile ("/home/findme/Desktop/result.txt");
outfile << "sha256('"<< c << "'):" << output1 << endl;
outfile.close();
}
ok so just an insight of what happens, it reads the context of the file named text.txt in this file there is 3 lines which are (test, test2, test3). so it reads one line and sends it to the function and it returns then it does it for the second line and so on. Once it hits the last line the cout function reads this
sha256('test'):9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
sha256('test2'):60303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752
sha256('test3'):fd61a03af4f77d870fc21e05e7e80678095c92d808cfb3b5c279ee04c74aca13
and that's correct. there is no problem whatsoever with the cout function but when I try to put it in an external file named result.txt it displays
sha256('test3'):fd61a03af4f77d870fc21e05e7e80678095c92d808cfb3b5c279ee04c74aca13
That's is the problem, it seems like all it does is writes the last function. I tried to change the placing of the ofstream as in outside the loop or before the cout function and it gives the same issue, I also tried with a loop and it gives the same issue. It just repeats the sha256('test3').
Now I know that its obviously something really easy to fix but im not seeing, therefore can someone please help me? I want it to display exactlty what the cout displays. (there are no error messages and it compiles without a problem). Thank you
You are creating your ofstream outfile in the loop. Meaning it's recreated each iteration through the loop. If you move this line to the top of your sample code it should work:
ofstream outfile ("/home/findme/Desktop/result.txt");

ofstream creating file but not writing to it in C++

I am writing an MFC program that has a dialog with an "Export" button that will take all of the data that has been entered into the file and export it to a .txt (at some point I want to change this to a .msg file...but that's a question for another day).
However, when I click the button, it creates the file but doesn't write anything inside the file. For testing, I removed everything except just a simple literal string and even that isn't printing. Here is the current code for that event: The myfile.flush() statement is leftover from when I had a loop that I was trying to print to the file.
void CEHDAHTTimerDlg::OnBnClickedExport()
{
// in this function, we want to export the items to a text file
std::ofstream myfile("TodayTime.txt");
myfile.open("TodayTime.txt");
if (myfile.is_open())
{
myfile << "The average call time is ";
myfile.flush();
myfile.close();
}
else
{
SetDlgItemText(IDC_EXPORT, L"Export Unsuccessful! -- No File");
}
}
Is there anything you all can think of that could be causing this? I've been at it for a few hours trying different things, like utilizing a myfile.write() function instead. I've searched a lot around here, reddit, and google in general trying to find out why this isn't writing.
I appreciate your help.
EDIT:
Okay, calling the myfile constructor the way that I did, by including the file name, went ahead and did what open file would have done
Thanks for your help!
commenting out the redundant "open" solves it.
#include <iostream>
#include <fstream>
int main()
{
// in this function, we want to export the items to a text file
std::ofstream myfile("TodayTime.txt");
// myfile.open("TodayTime.txt");
if (myfile.is_open())
{
myfile << "The average call time is ";
myfile.flush();
myfile.close();
}
else
{
std::cerr << "didn't write" << std::endl;
}
}
I strongly suspect that you're invoking undefined behaviour by opening and already-open stream.
Here is the explanation:
The call to myfile.open("TodayTime.txt"); will fail because the stream is already associated with the file, setting the failbit.
The call to is_open() will succeed, as the file is open.
Then the call to streaming operator << will fail (because of the failbit).
this is because myfile << "The average call time is "; not working
to fix that
std::ofstream myfile;
myfile.open("TodayTime.txt",std::ios:app) //app for appending you can use trunc for
//truncating file
//for flushing content's of existing file use myfile.flush();
if (!data_pack.is_open())
{
std::cerr << "unable to open the file for writing";
}
myfile << "some stuff tho write you can replace the string with variable"
<<std::endl; //for next line
//at last close file
myfile.close();