C++: std::ofstream method open() wipes open ifstream file on second iteration - c++

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;
}

Related

Error opening a text file using ifstream c++11

I am creating an object called SpellChecker that corrects the spelling of words in a string.
To check if the words are spelled correctly and if not to correct them, I have a text file of correct words (one per line). I also have a text file of words that are misspelled and their corrections separated by a tab.
My issue is reading in my text file. I have created an if statement to see if my file opens successfully. However, I believe my file should be readable and it is not. I am trying to find out why this is happening.
Here is my SpellChecker constructor:
SpellChecker::SpellChecker(string tempLanguage, string correctWordsFile,string wordCorectionsFile){
language=tempLanguage;
ifstream istream;
istream.open(correctWordsFile);
if(!istream.is_open()){
cout << "Error opening " << correctWordsFile << endl;
}
int count=0;
string temp;
while(!istream.eof()){
getline(istream,temp);
correctWords[count] = temp;
count++;
}
numCorrectWords = count;
istream.close();
istream.open(wordCorectionsFile);
if(!istream.is_open()){
cout << "Error opening " << wordCorectionsFile << endl;
}
int j=0;
int i=0;
char temp2;
while(!istream.eof()){
istream.get(temp2);
if(temp2 == '\t'){
j++;
}
else if(temp2 == '\n'){
i++;
j = 0;
}
else
wordCorections[i][j] += temp2;
}
numwordCorrections = i;
istream.close();
}
Here is my main:
int main(){
SpellChecker spellCheck("English","CorectWords.txt","WordCorections.txt");
spellCheck.viewCorrectWords();
spellCheck.viewCorrectedWords();
spellCheck.setEnd('~');
spellCheck.setStart('~');
cout << spellCheck.repair("I like to eat candy. It is greatt.");
}
The terminal returns:
"Error opening CorectWords.txt"
How can I solve this problem?
The call to library function is_open() is returning false, which could be due to one of many reasons.
Ensure that :
1. You have used correct name of the data file.
2. The data file is in the same folder as the executable of your program.
3. It has been closed by any previous program that read it.

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.

C++ file handling, is_open returning bad

If I include the if test in my code the error message is returned and I'm not sure why.
and when it's not used, my program get's stuck in a loop where it never reaches the end of the file. I don't understand what's going wrong.
int countlines()
{
fstream myfile;
myfile.open("questions.txt", ios::in);
string contents;
int linenumber = 0;
//if (myfile.is_open())
// {
while (!myfile.eof())
{
getline( myfile, contents );
if (contents != "")
{
linenumber++;
}
}
cout << "there are " << linenumber << " lines.\n";
//}else {cout<<"Unable to get file.\n";}
myfile.close();
return(linenumber);
}
What's going on is that your file is not being opened. That's why is_open fails.
Then, when you comment out the check, you're breaking your loop because you're iterating incorrectly (see my comment) and not detecting stream failures (.eof() will never be true on that stream).
Make sure that the file is in the right place, and that it is accessible.
The correct idiom for reading a file line-by-line in C++ is using a loop like this:
for (std::string line; std::getline(file,line);)
{
// process line.
}
Inserting this in your example (+fixing indentation and variable names) gives something like this:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Count the lines.
int count = 0;
for (std::string line; std::getline(file,line);)
{
if (!line.empty()) {
++count;
}
}
return count;
}
Note that if you don't intend to process the line contents, you can actually skip processing them using std::streambuf_iterator, which can make your code look like:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Refer to the beginning and end of the file with
// iterators that process the file character by character.
std::istreambuf_iterator<char> current(file);
const std::istreambuf_iterator<char> end;
// Count the number of newline characters.
return std::count(current, end, '\n');
}
The second version will completely bypass copying the file contents and avoid allocating large chunks of memory for long lines.
When using std::istream and std::ostream (whose std::fstream implements), the recommended usage is to directly use the stream in a bool context instead of calling eof() function because it only return true when you managed to read until the last byte of the file. If there was any error before that, the function will still return true.
So, you should have written your code as:
int countlines() {
ifstream myfile;
int linenumber = 0;
string linecontent;
myfile.open("question.txt", ios::in);
while (getline(myfile, linecontent)) {
if (!linecontent.empty()) {
++linenumber;
}
}
return linenumber;
}
Try the following code. It will also (hopefully) give you an idea why the file open is failing...
int countlines()
{
ifstream myfile;
myfile.open("questions.txt");
string contents;
int linenumber = 0;
if (myfile.is_open())
{
while (getline(myfile, contents))
{
if (contents != "")
linenumber++;
}
cout << "there are " << linenumber << " lines." << endl;
myfile.close();
}
else
cout << "Unable to get file (reason: " << strerror(errno) << ")." << endl;
return linenumber;
}