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.)
Related
/* I want to transfer the content from the first file ("constituencies") to the second ("temp") and then delete the original ("constituencies") file then rename() ("temp") to ("constituencies") so it can be refreshed with a specific entry/line be deleted from the file by first finding it and then not adding it in the newly generated file ("temp") and then deleting the old ("constituencies") file and renaming ("temp") as the new ("constituencies") but both of my files get deleted in the process, please help */
void updateOrDelete()
{
fstream constituencies("constituencies.txt", ios::out | ios::in);
fstream temp("temp.txt", ios::out | ios::in);
string deleteline;
string line;
cout << "Which constituency do you want to remove? \n";
cin >> deleteline;
while (getline(constituencies, line))
{
if (line != deleteline)
{
temp << line << endl;
}
}
constituencies.close();
temp.close();
remove("constituencies.txt");
rename("temp.txt", "constituencies.txt");
}
This line fails if temp.txt does not exist:
fstream temp("temp.txt", ios::out | ios::in);
but you don't notice that since you don't check if it was successful.
Remedy:
ifstream constituencies("constituencies.txt");
if(constituencies) {
// constituencies.txt successfully opened for reading
ofstream temp("temp.txt");
if(temp) {
// temp.txt successfully opened for writing
} else {
// fail
}
} else {
// fail
}
Here's a full version of the function with some added error checking and the user interaction moved out so you'll have to call it with the deleteline you want to remove from the file.
// returns true if successful and false otherwise
bool updateOrDelete(const std::string& deleteline) {
static const char* orig = "constituencies.txt";
static const char* temp1 = "temp1.txt";
static const char* temp2 = "temp2.txt";
// make sure you are the only one processing the file
if(std::rename(orig, temp1) == 0) {
// open for reading and check if it succeeded
if(std::ifstream in(temp1); in) {
// open for writing and check if it succeeded
if(std::ofstream out(temp2); out) {
std::string line;
// do your processing
while(std::getline(in, line)) {
if(line != deleteline) {
out << line << '\n';
}
}
// check if the new file is still in a good state before closing it
bool success = static_cast<bool>(out);
out.close();
in.close();
if(success) {
// all's well - move the processed file into place
// and remove the old (renamed) original
return std::rename(temp2, orig) == 0 && std::remove(temp1) == 0;
}
// processing failed
// remove the corrupt/truncated file
std::remove(temp2);
}
}
// try to restore the original file
std::rename(temp1, orig);
}
// if we get here, something failed
return false;
}
I am trying to build a "fileUpdater" which will copy an original file into multiple directories, where a file with the same name and extension was previously found.
bool update_files(const string inputPath, const vector<string> outputPaths)
{
ifstream src(inputPath);
if(!src.is_open())
{
cout << "Unable to open input file\n" << inputPath <<endl;
return false;
}
else
{
ofstream dst;
for(unsigned int i=0; i<= outputPaths.size()-1; i++)
{
dst.open(outputPaths[i]);
try
{
dst << src.rdbuf();
dst.close();
}
catch(int e)
{
cout << "Unable to replace file\n" <<endl;
cout << outputPaths[i] <<"\n"<< endl;
cout << "Error code: " <<e<<endl;
}
}
};
src.close();
return true;
}
Exactly after executing
dst.open(outputPaths[i]);
in the second iteration, the original file opened by
ifstream src(inputPath);
gets wiped and only an empty file is copied into the remaining directories.
I also tried
dst.clear();
dst.close();
and
src.clear();
src.seekg(0,ios::beg);
before entering the next iteration, but it made no difference.
UPDATE
After trying different files, I realised the behavior depends on the input file. Above behavior appeared for .m-files (MatLab).
After testing it with .txt files, all files were wiped.
The way you're copying the file, with dst << src.rdbuf();, will leave the current file position at the end of your input file. On the second iteration, that same read won't read anything (leaving an empty copy of the file) because you're already at the end of the input file.
The solution is to seek back to the beginning of the input file before every read, using seekg. You should call tellg before reading anything (right after opening the file), then seek to that position.
auto startpos = src.tellg();
ofstream dst;
// ...
src.seekg(startpos);
dst << src.rdbuf();
None of the proposed methods work.
Neither resetting the pointer, nor pulling ifstream into the loop, which would result in opening the input file (which is not supposed to change) unnecessarily often.
It is still unclear why dst.open(outputPaths[i]); is wiping the input file. Also the exact moment of the wipe depends on used types of files.
I implemented following workaround, effectively reading the input file into a string and closing it beforehand, in order to protect it from further read/write action.
bool update_files( const string inputPath, const vector<string> outputPaths)
{
const char * in = inputPath.c_str();
ifstream src(in);
if(!src.is_open())
{
cout << "Unable to open input file\n" << inputPath <<endl;
return false;
}
else
{
string buffer;
streamsize s=src.gcount();
src.seekg(0,ios::end);
buffer.reserve(src.tellg());
src.seekg(0,ios::beg);
buffer.assign((istreambuf_iterator<char>(src)), istreambuf_iterator<char>());
src.close();
for(unsigned int i=0; i<= outputPaths.size()-1; i++)
{
const char * out = outputPaths[i].c_str();
ofstream dst(out);
try
{
dst << buffer;
dst.close();
}
catch(int e)
{
cout << "Unable to replace file\n" <<endl;
cout << outputPaths[i] <<"\n"<< endl;
cout << "Error code: " <<e<<endl;
}
}
};
src.close();
return true;
}
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.
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");
wFile.open(fileName, ios::out | ios::trunc);
if (!(wFile.is_open()))
{
cout << "Error in opening the file" << endl;
return false;
}
else
{
for (unsigned int i = 0; i < Widgets.size(); i++)
{
Widgets.at(i)->copyToBinary(fileName);
}
wFile.close();
return true;
}
I'm trying to copy different objects types from a vector. My problem is that when this code runs for the copy, it only copies the last object. It seems like the code just overrides the existing text.
Also, I have this code in each class (this is the copyToBinary function):
ofstream file(fName);
file << *this;
file << endl;
What am I missing here?
Problem:
You pass the finename to copyToBinary(), reopening a stream and overwriting it.
Solution:
Pass the wFile stream as reference and write to it without reopening each time
void copyToBinary(ostream& file) {
file << *this;
file << endl;
}
You should not reopen the file in copyToBinary. Pass wFile as argument not filename :
...copyToBinary(ostream &file) {
file << *this;
file << endl;
}
and call ...copyToBinary(wFile).
First, when you are opening the file
wFile.open(fileName, ios::out | ios::trunc);
Everytime it is called the existing content get lost as you are using trunc mode. It will be better if you use ios::app mode. So that whenever the above code runs, it opens the file in append mode.
Second, You are passing the filename in the copyToBinary function. And By using the default constructor
ofstream file(fName);
your file is getting opened everytime in default ios::out mode but not in ios::app mode. It will be better either you pass the refernece of opened file or open the file in function as append mode.