Why using while(!input.eof()) loop twice not working? [duplicate] - c++

This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 9 years ago.
On the following lines of code is intended to put every words in the input text file(words are separated by new lines) to a vector of strings, then to turn each word inside out, and to see if this turned word is contained in the list of words in the input file.
I believe my binary search function and wordTurn function works fine.
I did several simple tests on my code, and I found out using while(!myFile.eof()) loop twice might be the cause for my code not working. By not working I mean I get the output file("pairs.txt") as an empty document(it is supposed to be a list of pairs of words).
That is, when I put some simple print code in the second while(!myFile.eof()) loop body, it did not get printed out, from which I concluded this loop is not reached. This is more likely, since it printed when I commented out the first while(!myFile.eof()) loop. I originally placed the first while loop at the else body, but this made no difference.
What do you think is the problem?
I tried combining those two loop body into the second loop, and it produces something in the output file, but this was not what this code was supposed to do, and this was logically not correct.
Any words of advice would be greatly appreciated.
int main(int argc, char* argv[]) {
vector<string> words;
ifstream myFile(argv[1]);
ofstream outputFile("pairs.txt");
string vocab;
string s;
int count;
while(!myFile.eof()) { //first while(!myFile.eof()) loop
getline(myFile, s);
words.push_back(s);
}
if(argc != 2) {
cout << "Usage: provide the name of one input file after the dictlookupHN executable file." << endl;
return (1);
}
else {
if(!myFile.is_open()) {
cerr << "Error: unable to open file " << argv[1] << endl;
return (1);
}
else {
while(!myFile.eof()) { //second while(!myFile.eof()) loop
getline(myFile, vocab);
string turnedWord = wordTurn(vocab);
if(binsearch(words, turnedWord) != "") {
outputFile << vocab << ":" << turnedWord << endl;
count++;
}
}
}
}
myFile.close();
outputFile.close();
return 0;
}

The ifstream class maintains an internal offset into the stream data, keeping track where it has to read from, for the next operation.
When this offset reaches the end, it stops the first loop, (eof() returns false). You need to reset this internal position back to the beginning of the file, before reading again.
You do that by saying:
myFile.clear(); // clear stream flags and error state
myFile.seekg(0, ios::beg); // reset read position
before the second loop.
Edit: Added call to clear().

What is happening is that the seek pointer for the file is at the end of the file when you complete the first run through the file. If you want to seek through the file again, you will need to reset the file pointer using myFile.seekg(0, ios::beg); before the second while loop. Something like this:
...
else {
myFile.seekg(0, ios::beg);
while(!myFile.eof()) { //second while(!myFile.eof()) loop
...
That should fix the problem.

If you want to read the file again [once you have read the entire file to the end] you will need to reset the bad/fail bits, and also seek to the start. End of file is a permanent state unless something is done to reset it.
So, you need myFile.clear(); and myFile.seekg(0, ios_base::beg);
Although I have a feeling you actually want to open a different file in this particular case, in which case, I would recommend closing the current one, and using a different variable - they aren't very expensive, and it makes the code clearer [particularly if you call it something sensible, e.g. vocabFile instead of myFile]

To read from the beginning of your file again, use:
myFile.seekg(0, ios::beg);
But note, that this is not a good way of reading from a file:
string vocab;
while(!myFile.eof()) {
getline(myFile, vocab);
...
}
This is how it should look like:
string vocab;
while(getline(myFile, vocab)) {
if (vocab.empty())
continue; // empty line might be read
...
}
Have a look at: Why does my input seem to process past the end of file?

The first loop reaches the end of file, and you're not resetting before the second read. You need to seek to the start of the file after the first loop: myFile.seekg (0, ios::beg); will do this for you.

the first loop reached EOF, thats the position of "read marker", so the second EOF is readed as first in the second loop.
As simple, try to close and open file between loop.
Better idea is to use seekg to read from top again.

Thank you everyone for all of these words of good advice.
They really helped me understand how the ifstream and eof work.
My TA's advice was to use for loop instead of the second while(!myFile.eof()) loop. I might be using this option, but your answers will be of great help when I have to use eof twice.
Thank you!
while(!myFile.eof()) {
string s;
getline(myFile, s);
words.push_back(s);
}
for(int i=0; i<words.size(); i++) {
//LOOP INV:
string vocab = words.at(i);
string turnedWord = wordTurn(vocab);
if(binsearch(words, turnedWord) == 1 && turnedWord.length() > 3) {
outputFile << vocab << ":" << turnedWord << endl;
count++;
}

Related

Cout empty when there are two getline?

I am trying to cout my array but it keep showing empty whenever I try to.
int array_size = 0;
int pos;
string line;
ifstream inFile;
string filename = 'test.txt';
inFile.open(filename);
for(array_size = 0; getline(inFile, line); array_length++);
string * array_holder = new string[array_size];
while(getline(inFile, line))
{
array_holder[pos++] = line;
}
printArray(array_holder, array_size);
Print array
void printArray(string * data, int x)
{
for(int z = 0; z < x; z++)
cout << data[z] << endl;
}
When I comment out this line:
for(array_size = 0; getline(inFile, line); array_length++);
The printArray will show all the contents inside.
But when I uncomment the line, it just show empty lines (depending on the array size).
I reckon this is due to double getline? Do I need to clear the getline buffer or something? I couldn't find any details about getline buffer except for cin. Or is this memory issue?
Note that I cannot use vector in my situation and I need to read the total number of lines in the text file in order to parse the array size to my array syntax.
Thanks.
for(array_size = 0; getline(inFile, line); array_length++);
This loop reads inFile, one line at a time, until the end of the file is reached.
After this loop terminates, the entire file has been read. After all, that's exactly what your program tells your computer to do: read one line at a time, incrementing the counter, until getline fails, which happens at the end of the file. But then immediately afterwards, the second loop attempts to continue reading from the file:
while(getline(inFile, line))
However, since the end of the file has already been reached, this immediately fails, and nothing happens.
You obviously want to reread the file from the beginning, here. However, the Golden Rule Of Computer Programming states "your computer will always do exactly what you tell it to do, instead of what you want it to do". You have not told your computer to go back to the beginning of the file here, so it has no reason to do that. So, you simply have to tell your computer to do that, by closing and reopening the file, or by using the seekg() method.

ifstream and ofstream: How do I perform multiple modifications to a file?

I've been googling this question for a few hours and can't seem to find anything that addresses it.
I'm reeaaaally hazy on file operations in C++, but I've spent about 20 of the last 36 hours reading documentation and forum questions trying to get a project for a friend together.
Say I've got a file called raw_questions.txt, and I'd like to make some changes to it. This file is a study guide for an exam, and has a question followed by 4 multiple-choice answers. I want to remove blank lines and add some tokens to allow another program I'm working on to parse it. I've written a formatter program to perform those operations. The operations are:
Remove blank lines from source file as it appears it's double-spaced
Add a delimiter character ('#') to the end of each question and
answer.
Using the delimiter, read each question and answer in as a string
and append it to an output file with a token at the beginning of
question or answer, which will let my other program know whether a
line contains a question or answer.
My question: I'm stuck at how to move from one operation to the next. My current approach is to read each line into a string, perform the operation on the string, and to add the new string to an output file. Using this approach, to perform the next operation I have to open the previous operation's output file as my new input file, and make a new output file for that operation. I feel like there's got to be a better way, but like I said, I'm pretty hazy on file operations in C++. What should I be doing in this situation?
I've considered creating an ifstream and ofstream that both point to the same file, and hoping that when the ifstream file is opened, it will store a temporary copy in memory. Then, after I read line by line and write to my ofstream object, when it closes it will overwrite my old file. I don't know if that makes any sense, and I don't think that's even how fstream works.
The code that I have so far:
#include <fstream>
#include <string>
#include "Debug.h"
Debug debugger;
void remove_empty_lines (std::ifstream& input, std::ofstream& output);
void insert_delimiter (std::ifstream& input, std::ofstream& output, char delimiter);
void create_output (std::ifstream& input, std::ofstream& output);
int main() {
debugger.set_active();
char delimiter = '#';
std::ifstream input;
std::ofstream output;
input.open("questions_source.txt");
output.open("questions_intermidiate.txt");
remove_empty_lines (input, output);
}
void remove_empty_lines (std::ifstream& input, std::ofstream& output) {
while (!input.eof()) {
std::string line;
std::getline(input, line);
if (line != "") {
output << line << std::endl;
}
}
}
void insert_delimiter(std::ifstream& input, std::ofstream& output) {
}
// This function doesn't quite work, WIP - Please ignore
void create_output(std::ifstream& input, std::ofstream& output) {
std::string line;
for (int i = 1; !input.eof(); i++) {
debugger.out("Inserting tokens.");
bool found = false;
while (!found) {
getline (input, line);
if (i < 10) {
if (line[1] == ')') {
line.erase (0, 3);
output << "[" << i << "]" << line << std::endl;
debugger.out("Found line: " + line);
found = true;
}
} else if (i < 100) {
if (line[2] == ')') {
line.erase (0, 4);
output << "[" << i << "]" << line << std::endl;
debugger.out("Found line: " + line);
found = true;
}
}
}
for (int j = 0; j < 4; j++) {
getline (input, line);
if (line[1] == ')') {
line.erase (0, 3);
output << "[" << i << "a]" << line << std::endl;
}
}
}
}
I'm also trying to teach myself git at the moment, so I happen to have the project I'm working on hosted on github here. I don't know if the context will make what I'm trying to do make sense, but I'm posting it just in case.
Bonus question: I've been racking my brain, but I haven't come up with a solution to adding the delimiter. Answers seem to be one line long, so I can probably just add the delimiter to the end of any line starting with "A)" etc., but some of the questions are much longer. My thought is to find any occurrence of "A)" and add the delimiter to the end of the line above it, but I can't think of how to do that. Can anyone point me in the right directions for member functions of fstream that might help?
Thanks for reading.
Streams do not magically read the entire file into memory. If that is what you want to do, you should just do that: my guess is that your file is considerably smaller than your available memory, and it might be easier to perform all the operations in place using standard C++ containers.

how to detect end of file for string inputs in C++

//Stores the line
string line;
//create a vector where each element will be a new line
vector<string> v;
int counter = 0;
//While we havent reached the end of line
while (getline(cin, line) && !cin.eof())
{
//get the line and push it to a vector
v.push_back(line);
counter++;
for(int i = 0; i <counter; i++)
{
cout<<v[i]<<endl;
}
}
return 0;
}
The problem is, how come if i input lets say:
Hello
World (end of file)
The output is only:
Hello
The World is not outputted it only outputs both Hello and World if I input
Hello
World
(end of file)
Sorry if this is a really simple question :/ but i can't figure this out
If you have a line that ends with EOF without end of line, this:
while (getline(cin, line) && !cin.eof())
will have a getline that returns "all ok", but since the getline got to the actual end of file, cin.eof() is also true, meaning that your loop will not process the LAST of your input.
Change the code so that it simply does:
while (getline(cin, line))
and all will be fine.
If you really care that you actually read the whole file, and getline didn't fail for some arbitrary other reason, then using something like this AFTER the loop may ensure that - but I find it rather hard to think of a case where this would happen...
if (!cin.eof())
{
cout << "Enexpected: didn't reach end of file" << endl;
}

C++ text decoder only taking one line from a text file

Im working on a two part program that uses an encoder do encode a text file then a decoder to decode the text file. However i cannot get my decoder to read the whole text file it just reads the first line. How do i fix this, ive played around with the loops but it is not helping me.
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main()
{
ifstream fin;
ofstream fout;
string lineFromFile;
fin.open("secret.txt");
if (!fin.good()) throw "I/O error";
fout.open("secret.txt", ios::app);
if (!fout.good()) throw "I/O error";
while (fin.good())
{
getline(fin, lineFromFile);
for (int i = 0; i < lineFromFile.length(); i++) // for each char in the string...
lineFromFile[i]--; // bump the ASCII code by 1
fout << lineFromFile << endl;
}//while
fin.close();
fout.close();
return 0;
}
You are closing the stream in the first iteration (i.e., after reading the first line). Then you open the output stream to write the first line. Only then do you reach the end of the while-loop, at which time fin.good() cannot be true anymore since you closed fin.
The loop should contain only the reading and writing. Opening done before the loop, closing done after.
Additional suggestion: Use proper indenting, it helps understanding the control flow.
I think the problem might be that you are reading and writing from the same file: secret.txt. I do not know what the expected behaviour is but when I ran the code it was infinite, which makes some sense as you are appending.
Try changing the ouput stream, fout, to a different file name.
The while loop that currently processes the file will process a final invalid read as you do not check if getline() was successful until the loop condition. I would suggest changing to:
for (;;)
{
getline(fin, lineFromFile);
if (!fin.good())
{
break;
}
for (int i = 0; i < lineFromFile.length(); i++)
lineFromFile[i]--;
fout << lineFromFile << endl;
}
Perhaps that because another file handle,fout, accesses the file to which fin is tied, fin is closed as a side-effect. Either that, or because fout appends to the file, the file pointer is pointed to the end of the file, and so because there is no more input to read, the loop ends.
(These are hypotheses; I am not familiar enough with the specifics of C++ I.O. to conclusively say what is happening.)
If you are supposed to replace the original file with an encoded version, I recommend setting the file tied to fout to a temporary file for the duration of your code above, closing both fin and fout, and then copying the file associated with fout over the file associated with fin.
Otherwise, just tie fout to a different filename than fin, such as "encoded.txt".

I am reading file but while reading,it is reading last character one time extra?

I am trying to read file by following code
void main()
{
int i=0;
ifstream fout1 ("Aj.txt",ios::binary);
if (fout1.is_open())
{
while(fout1)
{
i++;
fout1.get(ch1);
cout<<ch1;
}
cout<<i;
fout1.close();
}
else
{
cout << "Unable to open file";
}
return 0;
}
here this code reads whole file but it goes into loop one extra time i.e if there are 4character in file then in loop it will iterate 5 time .how to correct it or overcome it.
That code reads until the eof flag is set, but the eof flag is not set until a read actually fails. Which means when fout1.get(ch1) reads the last character, eof is not set yet. So it displays, and checks fout which is still good, so it re-enters the loop. Then fout1.get(ch1) fails because of eof, and it displays the previous value of ch1 again. The correct way to write that loop is:
while(fout1.get(ch1)) { //false when it can't read any more characters
i++;
cout<<ch1;
}
cout<<i;
fout1.close();
After reading the 4th character the stream is still in a good state, so it tries to read a 5th character and at that time it encounters the end of file. You should check the stream state after calling get. For instance:
while( fout1.get(ch1) )
{
cout << ch1;
++i;
}
This is an easy mistake to make if you're not familiar with iostreams. stream.eof() will be false if you have merely read the last character in the file. It only becomes true when you try to read past the end of the file. You need an inner loop that looks like this:
for (;;)
{
fout1.get(ch1);
if (!fout1) break;
cout << ch1;
i++;
}