overwriting data in a .txt file c++ - c++

I have a functions overwrites data on an existing file, however it isn't working properly here is my code:
void printList(entry* my_node)
{
ofstream output;
output.open("output.txt");
std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
if(my_node == NULL) return;
else {
string x=my_node->forename;
output<<x<<endl;
output<<my_node->surname<<endl;
output<<my_node->email<<endl;
output<<my_node->number<<endl;
std::cout<<"forename: "<<my_node->forename<<std::endl;
std::cout<<"surname: "<<my_node->surname<<std::endl;
std::cout<<"email: "<<my_node->email<<std::endl;
std::cout<<"Phone Number: "<<my_node->number<<std::endl;
printList(my_node->next);
output.close();
}
}
when i do this it clears the file and don't write anything to it ...i have also tried to first clear the file and then write to it
output.clear();
output.close();
output.open("output.txt",ios ::out|ios::app);
std::streambuf *coutbuf = std::cout.rdbuf();
//save old buf
///and then the rest but it didn't work as well
Any help please????

You call printList recursively with each next value until you reach the end of the list, which calls printlist(NULL). Then the output file is truncated again for the last time. This is the reason why the file is empty.
To write the whole list to the file, use a loop and inside the loop write each element, e.g.
while (my_node != NULL) {
output << my_node->forename << endl;
output << my_node->surname << endl;
output << my_node->email << endl;
output << my_node->number << endl;
/* ... */
my_node = my_node->next;
}
output.close();

You open and therefore overwrite the new file every time you reach printList, even in the case where my_node is null. If that is not your intention, then move
ofstream output;
output.open("output.txt");
into the body of the if statement. Or if you would like all nodes content in the same file, move it out of the traversal.
Also note that ofstreams contructor takes arguments, so you can write
ofstream output("output.txt");

Related

How to write string stream to ofstream?

I am trying to write a stringstream into a file but it not working.
int main() {
std::stringstream stream;
stream << "Hello world";
cout << stream.rdbuf()<<endl;//prints fine
std::ofstream p{ "hi.txt" };
p << stream.rdbuf();//nothing is writtten
p.close();
std::ifstream ip{ "hi.txt" };
std::string s;
ip >> s;
cout << s;//nothing printed
p.close();
return 0;
}
This answer here follows the same process. But it's not working in my case.
Internally, the stringstream maintain a read pointer/offset into its data buffer. When you read something from the stream, it reads from the current offset and then increments it.
So, cout << stream.rdbuf() reads the entire stream's data, leaving the read pointer/offset at the end of the stream. Thus, there is nothing left for p << stream.rdbuf() to read afterwards. In order to do that, you have to seek the read pointer/offset back to the beginning of the steam, eg:
cout << stream.rdbuf() << endl;
stream.clear(); // resets the error state, particularly eofbit
stream.seekg(0);
p << stream.rdbuf();
The C++ file streams use input and output pointers to keep track of it's data buffer. When you write something it is written from the position where the current write pointer is or we can say the write pointer offset is added and then the data is simply written, after which the write pointer is incremented by that much value
The stream.rdbuf() function reads all the data in the current file and leaves the read pointer at the end of the file so when we run the same function again there is nothing to read
This can Simply be solved by using stream.seekg(0)
cout << stream.rdbuf() << endl;
stream.seekg(0); //This puts the pointer at the beginning of the file
p << stream.rdbuf(); //Should work'

c++ file pointers not working properly

I'm trying to write a program that replaces a specific number with an 'x' character. The task requires every number to be in its own line, but it seems like '\n' is causing the read/write pointers to behave out of this world. Here's a picture of the output.
My questions are:
why are the pointers behaving this way?
How far do I need to move the write pointer backwards to overwrite a line to make this work?
is there an easier workaround?
Here's my code:
void input(int n)
{
fstream file;
file.open("numbers.txt", ios::out);
while(n --> 0)
{
file << n;
file << '\n';
}
file.close();
}
void read()
{
fstream file;
string tmp;
file.open("numbers.txt", ios::in);
while(true)
{
getline(file,tmp);
if(file.eof())
break;
cout << tmp << endl;
cout << "tellg: " << file.tellg() << " tellp: " << file.tellp() << endl;
}
file.close();
}
void replace()
{
fstream file;
string tmp;
file.open("numbers.txt", ios::in | ios::out);
while(true)
{
file >> tmp;
if(tmp == "6")
{
//cout << file.tellg() << endl;
file.seekp(file.tellg() - tmp.length()-1);
file << "x";
}
if(file.eof())
break;
}
file.close();
}
int main()
{
input(10);
replace();
read();
return 0;
}
Since you open your file in text mode, you need to account for the potential that the underlying stream may use a line end sequence (\r\n) rather than just a \n. I guess, this is the primary problem. The easiest remedy is probaly to open the file in binary mode:
file.open("numbers.txt", std::ios_base::binary | std::ios_base::in | std::ios_base::out);
That said, since you switch from writing to reading without intervening seek, your code is undefined behavior, i.e., anything can happen. You should seek to the current location between writing and reading.
Personally, I'd refrain from rewriting files in-place. It generally gets unnecessary trick. If I were to rewrite files in place, I'd use seekg() to get the current position before a read, saving the position and restoring it prior to the write (I essentially never use the seek operations, i.e., I may have got the signatures wrong):
for (std::streampos pos = (in >> std::ws).tellg();
in >> tmp; pos = (in >> ws).tellg()) {
if (need_to_overwrite) {
in.seekp(pos);
// ...
in.seekg(0, std::ios_base::cur);
}
}
The use of in >> std::ws is to make sure that whitespace is skipped before storing the position.
Also note that your check for file.eof() is wrong: the last line is processed twice. When reading from a file the result shall be tested before using the read string, e.g.:
while (in >> tmp) {
// ...
}

edit: trouble checking if file is empty or not, what am I doing wrong?

Edit: changed my question to be more accurate of the situation
I'm trying to open up a text file (create it if it doesnt exist,open it if it doesnt). It is the same input file as output.
ofstream oFile("goalsFile.txt");
fstream iFile("goalsFile.txt");
string goalsText;
string tempBuffer;
//int fileLength = 0;
bool empty = false;
if (oFile.is_open())
{
if (iFile.is_open())
{
iFile >> tempBuffer;
iFile.seekg(0, iFile.end);
size_t fileLength = iFile.tellg();
iFile.seekg(0, iFile.beg);
if (fileLength == 0)
{
cout << "Set a new goal\n" << "Goal Name:"; //if I end debugging her the file ends up being empty
getline(cin, goalSet);
oFile << goalSet;
oFile << ";";
cout << endl;
cout << "Goal Cost:";
getline(cin, tempBuffer);
goalCost = stoi(tempBuffer);
oFile << goalCost;
cout << endl;
}
}
}
Couple of issues. For one, if the file exist and has text within it, it still goes into the if loop that would normally ask me to set a new goal. I can't seem to figure out what's happening here.
The problem is simply that you are using buffered IO streams. Despite the fact that they reference the same file underneath, they have completely separate buffers.
// open the file for writing and erase existing contents.
std::ostream out(filename);
// open the now empty file for reading.
std::istream in(filename);
// write to out's buffer
out << "hello";
At this point, "hello" may not have been written to disk, the only guarantee is that it's in the output buffer of out. To force it to be written to disk you could use
out << std::endl; // new line + flush
out << std::flush; // just a flush
that means that we've committed our output to disk, but the input buffer is still untouched at this point, and so the file still appears to be empty.
In order for your input file to see what you've written to the output file, you'd need to use sync.
#include <iostream>
#include <fstream>
#include <string>
static const char* filename = "testfile.txt";
int main()
{
std::string hello;
{
std::ofstream out(filename);
std::ifstream in(filename);
out << "hello\n";
in >> hello;
std::cout << "unsync'd read got '" << hello << "'\n";
}
{
std::ofstream out(filename);
std::ifstream in(filename);
out << "hello\n";
out << std::flush;
in.sync();
in >> hello;
std::cout << "sync'd read got '" << hello << "'\n";
}
}
The next problem you'll run into trying to do this with buffered streams is the need to clear() the eof bit on the input stream every time more data is written to the file...
Try Boost::FileSystem::is_empty which test if your file is empty. I read somewhere that using fstream's is not a good way to test empty files.

I Can't print out a file that I wrote on

I have created a function to write some data on a text file, and it works fine. I created another function to read in all the content of the file, and print it out for me! But, it is not working for some reason. Could any one help please?
This is my function:
void myClass::displayFile() {
char line[LINE]; //to hold the current line
file.open("data.txt", ios::app);
//keep reading information from the file while the file is open and has data
while (!file.fail() && !file.eof()) {
int lineSize; //to loope through each line
file.getline(line, LINE);
lineSize = strlen(line);
//loop through the line to print it without delimiters
for (int i = 0; i < lineSize; ++i) {
if (line[i] == ';') {
cout << " || ";
} else {
cout << line[i];
}
}
}
file.close();
file.clear();
if (file.fail()) {
cerr << "Something went wrong with the file!";
}
}
Note: The function compiles and the loop is accessible, but the line string is empty.
This is the writing function:
void myClass::fileWriter() {
file.open("data.txt", ios::app);
file << name << ";" << age << ";" << "\n";
file.close();
file.clear();
}
Silly me, the cause of your problem was staring me right in the face from the beginning, and it's the app open-mode that's the problem. It is to open the file in write mode, which means you can't read from it.
And even if you could read from the file, the cursor is placed ad the end of the file the eofbit flag would have been set inside the first iteration anyway.
If you want to read from a file, then either use std::ifstream which automatically sets the in mode if you don't specify a mode, or you have to explicitly set the in mode when opening.

Writing to file results in empty file

I wrote a small program that opens a file, builds a vector from every line in the file and then have the user able to add/remove from the file. The program first removes from the vector, then rebuilds the file based on the vector. Here's the code that rebuilds the file (fileName is a member variable with the full name of the text file, ex. "test.txt":
bool rebuildFile() {
if (remove(fileName.c_str()) == 0) { // remove the old file
ofstream newFile(fileName); // create new file with same name
newFile.open(fileName, ios::app); // open to append to end of file
if (newFile.is_open()) {
newFile << fileHeader << endl; // add the first header line
for (int i = 0; i < myVector.size(); i++) { // loop through vector, adding strings to file
newFile << myVector[i] << endl; // I used "\n" instead of endl, but both give same results
}
newFile.close();
return true; // success
}
}
return false; // failure
}
After this function exits, the file is completely empty. So it clearly creates a new file, but then the writing part is an issue, and I can't figure out why. I read other posts where some had issues where they had the file open in Notepad/Notepad++, but I've always made sure to close that specific file before running the program. I'm not sure if the ios::app flag is causing an issue with the loop, but the documentation seems clear that it just points to the end of the file every time you output to it, so I don't think the issue is there. Any thoughts?
EDIT:
Apparently you can't append to an empty file... This new code works, but I'm not sure if there's a "cleaner" way to add to a file in two different ways without opening and closing it twice using different flags.
new code:
bool rebuildFile() {
if (remove(fileName.c_str()) == 0) {
std::ofstream newFile(fileName);
newFile.open(fileName);
if (newFile.is_open()) {
newFile << fileHeader << endl;
newFile.close();
}
newFile.open(fileName, std::ofstream::out | std::ofstream::app);
if (newFile.is_open()) {
for (int i = 0; i < myVector.size(); i++) {
newFile << myVector[i] << endl;
}
newFile.close();
return true;
}
}
return false;
}
Trying to call open on an already open file stream puts the stream in a failed state.
Just change
ofstream newFile(fileName); // create new file with same name
newFile.open(fileName, ios::app);
to
ofstream newFile(fileName, ios::app);
[ofstream.members]
void open(const char* s, ios_base::openmode mode = ios_base::out);
Effects: Calls rdbuf()->open(s, mode | ios_base::out). If that
function does not return a null pointer calls clear(), otherwise calls
setstate(failbit) (which may throw ios_base::failure (27.5.5.4)).
[filebuf.members]
basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode);
Effects: If is_open() != false, returns a null pointer. [...]
bool is_open() const;
Returns: true if a previous call to open succeeded (returned a
non-null value) and there has been no intervening call to close.
ofstream newFile(fileName);
doesn't just create the file, it opens it too. And that means you can't open it again.
I don't see a reason to remove the file, re-create it - truncating if it exists - and open it, write a little, close the file, open it again but for appending, and then write to it some more.
Plus, if you run out of luck, there's an opportunity for a different process to modify (or delete) the file between the first close and the second open, which is in general not a good thing.
This snippet should work:
bool rebuildFile()
{
std::ofstream newFile(fileName);
if (newFile)
{
newFile << fileHeader << endl;
for (int i = 0; i < myVector.size(); i++) {
newFile << myVector[i] << endl;
}
}
return newFile;
}
(Files are closed automatically, if needed, by ofstream's destructor.)