This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 4 years ago.
The following C++ code uses a ifstream object to read integers from a text file (which has one number per line) until it hits EOF. Why does it read the integer on the last line twice? How to fix this?
Code:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream iFile("input.txt"); // input.txt has integers, one per line
while (!iFile.eof())
{
int x;
iFile >> x;
cerr << x << endl;
}
return 0;
}
input.txt:
10
20
30
Output:
10
20
30
30
Note: I've skipped all error checking code to keep the code snippet small. The above behaviour is seen on Windows (Visual C++), cygwin (gcc) and Linux (gcc).
Just follow closely the chain of events.
Grab 10
Grab 20
Grab 30
Grab EOF
Look at the second-to-last iteration. You grabbed 30, then carried on to check for EOF. You haven't reached EOF because the EOF mark hasn't been read yet ("binarically" speaking, its conceptual location is just after the 30 line). Therefore you carry on to the next iteration. x is still 30 from previous iteration. Now you read from the stream and you get EOF. x remains 30 and the ios::eofbit is raised. You output to stderr x (which is 30, just like in the previous iteration). Next you check for EOF in the loop condition, and this time you're out of the loop.
Try this:
while (true) {
int x;
iFile >> x;
if( iFile.eof() ) break;
cerr << x << endl;
}
By the way, there is another bug in your code. Did you ever try to run it on an empty file? The behaviour you get is for the exact same reason.
I like this example, which for now, leaves out the check which you could add inside the while block:
ifstream iFile("input.txt"); // input.txt has integers, one per line
int x;
while (iFile >> x)
{
cerr << x << endl;
}
Not sure how safe it is...
There's an alternative approach to this:
#include <iterator>
#include <algorithm>
// ...
copy(istream_iterator<int>(iFile), istream_iterator<int>(),
ostream_iterator<int>(cerr, "\n"));
Without to much modifications of the original code, it could become :
while (!iFile.eof())
{
int x;
iFile >> x;
if (!iFile.eof()) break;
cerr << x << endl;
}
but I prefer the two other solutions above in general.
The EOF pattern needs a prime read to 'bootstrap' the EOF checking process. Consider the empty file will not initially have its EOF set until the first read. The prime read will catch the EOF in this instance and properly skip the loop completely.
What you need to remember here is that you don't get the EOF until the first attempt to read past the available data of the file. Reading the exact amount of data will not flag the EOF.
I should point out if the file was empty your given code would have printed since the EOF will have prevented a value from being set to x on entry into the loop.
0
So add a prime read and move the loop's read to the end:
int x;
iFile >> x; // prime read here
while (!iFile.eof()) {
cerr << x << endl;
iFile >> x;
}
At the end of the last line, you have a new line character, which is not read by >> operator and it is not an end of file.
Please, make an experiment and delete the new line (thelast character in file) - you will not get the duplication.
To have a flexible code and avoid unwanted effects just apply any solution given by other users.
int x;
ifile >> x
while (!iFile.eof())
{
cerr << x << endl;
iFile >> x;
}
Related
I was reading some old codes of mine and I realize that I don`t know exactly how ifstream works. The following code should just read a file, save its contents into an object and create another file with exactly same data, which is written as:
#include <iostream>
#include <fstream>
using namespace std;
class grade {
public:
float grade1;
float grade2;
float grade3;
};
void read_file(grade data[]){
ifstream myfile("data.txt");
int i=0;
while(myfile >> data[i].grade1){
myfile >> data[i].grade2 >> data[i].grade3;
i=i+1; }
myfile.close();
myfile.clear();
}
void write_file(grade data[]){
ofstream myfile("data_out.txt");
for(int i=0; i<3; i++){
myfile << data[i].grade1 << "\t" << data[i].grade2 << "\t" << data[i].grade3 << endl;
}
myfile.close();
myfile.clear();
}
int main()
{
grade data[3];
read_file(data);
write_file(data);
return 0;
}
With "data.txt" having:
23.5 10.1 11.6
14.3 8.2 9.3
6.5 6.7 5.3
The code works just fine, but I don't get how the ifstream "knows" which line or column it should be at a given moment, since the variable i is not used to control myfile. I am assuming that by default ifstream has two internal variables, one for line and the other for column, that each time a myfile >> xxx command is identified, the column variable is incremented and every time that a loop repeats the line variable is incremented.
It is something like that? Not actually controlling what line or column the code is at a given moment is quite confusing to me.
Lets say, for instance, that in this example I would like to read only the data on the second line and column. Could I access directly it using some explicit expression like 'myfile[1][1] >> xxx'? I guess that getlinecould be used, but since it is used for strings I really don't know.
In read_file() the reading occurs in this loop:
int i=0;
while(myfile >> data[i].grade1){
myfile >> data[i].grade2 >> data[i].grade3;
i=i+1; }
First, this assumes that data[] was already allocated with sufficient number of elements. It's strange since the file was not yet read, so how to know how many records are to be read in advance?
Then, this reading algorithm assumes that the data is in an ordinary text file, in which data elements are space separated:
The myfile >> data[i].grade1 reads the first grade of a new sequence. If it reaches the end of file, the result will be evaluated to false and the loop will end.
Then for every first grade read, the loops reads the next grades of the record: myfile >> data[i].grade2 >> data[i].grade3;
This logic reads in fact one number after the other. The only constraint is that there are one or several spaces between the numbers. The stream therefore doesn't care about columns: it's just the next item to be read.
The following input file would work equally well:
23.5
10.1
11.6
14.3 8.2
9.3 6.5 6.7 5.3
This being said, no need to close and clear the stream at the end of the function: when the function will return, its local objets will be destroyed. This includes the stream object, and the destruction ensures that everything ends right.
Remark: incomplete records are not handled well: if the first grade is present but one of the remaining is missing, the stream will be in an error state, and the grade2 or grade3 elements will be left uninitialized.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream in("input.txt");
int i = 0,sum=0;
char x;
while (!in.eof()){
in >> i;
if (in.good()) {
cout << "integer is " << i << endl; sum += i;
}
if (in.fail()) {
in.clear();
in >> x;
cout << "the char is " << x << endl;
}
}
cout << sum;
char z;
cin >> z;
}
and my input.txt is like:
bear: sdf 23 okI am fine 11q , 45
and my screen output is like:
the last number 45 doesn't show up
So what happened here? why 45 is regarded as one out of file. And if I add a 's' immediately right next to 45, the screen will have two s showing up, rather than just one.
The problem is that when extracting symbols for 45,it tries to extract symbol after '5' (to check if number continues further) and sees end of file, setting eofbit. This makes in.good() test to fail. Suggestions:
while (!in.eof()){
in >> i;
if ( in ) { //Not .good(), just conversion to bool
Or
while (!in.eof()){
if ( in >> i; ) { //both extraction and checking in same operation
Remember, .good() is not the same as checking stream state. .good() is telling if stream ready for further input. Bool conversion does ! .fail() and checks if last operation was executed succesfully
Well, "eof()" does work as expected, at least, how I expect it to work: it gets set when the stream somehow touches the end of file. If your file's last line actually ends with '5' rather than a newline, reading an integer stops due to touching newline and std::ios_base::eofbit gets set. Since std::ios::good() returns false if any bit is set, include std::ios_base::failbit the last value wouldn't be printed.
In general, eof() is more often misused than appropriately used. Essentially the only reasonable uses of eof() is to verify if the entire stream was consumed or to suppress an error message if eof() is set as it is expected that input fails at the end of the stream. Likewise, there is generally little use for std::ios::good().
I have a question on the stream behavior, see the following example. What I was expecting is, since there are only 5 chars in the string, and stream read will get stuck as I am trying to read 10 chars. Instead, the output is "hellooooo" ... the last char get repeated.
My questions are two folds: first, why? second, is there anyway to make stream behave as if no more repeating of last char?
#include <sstream>
#include <iostream>
using namespace std;
int main(void) {
char c;
string msg("hello");
istringstream iss(msg);
unsigned int i = 0;
while (i < 10) {
iss >> c;
cout << c;
i++;
}
cout << endl;
return 0;
}
What you see is the result of reading form a stream in an erronous state. When you read past the last element in the stream (this being a string stream), the stream becomes erroneous and any other attempt to read from it will fail (and leave the extraction variable untouched).
You will have to check if the extraction operation succeeded before reading further:
if (iss >> c) {
// succeess
} else {
// failed to extract, handle error
}
Were you to use a stream connected to the console (for an example) your call to >> would have blocked as you expected. The behavior of stringstream is different (you cannot expect to micraculously contain more data)
The reason is that when you've read to the end of the stream, all attempts to read after that just fail, leaving the last value read in your c.
If you want to read at most 10 characters:
while (i < 10 && is >> c) {
cout << c;
i++;
}
This works because a stream can be converted to bool, and it's true if the stream is in a "good" state.
"the last char get repeated"
When iss >> c fails, c stays unmodified.
Check whether extraction of value succeeded by directly evaluating this expression: if (iss >> c), but don't even think about calling iss.good(). Check this answer and also have a look at:
How does that funky while (std::cin >> foo) syntax work?
Why does my input seem to process past the end of file?
I understand that cin.eof() tests the stream format. And while giving input, end of character is not reached when there is wrong in the input. I tested this on my MSV C++ 2010 and am not understanding the strange results. No matter what I give the input, I am getting Format Error message that is present in the program.
#include <iostream>
using namespace std;
int main()
{
int i;
cin>> i;
if(!cin.eof())
{
cout<< "\n Format Error \n";
}
else
{
cout<< "\n Correct Input \n";
}
getchar();
return 0;
}
Results I expected:
Values for i =
10 => Correct Input but the output is Format Error
12a => Format Error
Could someone explain where I am going wrong. Thanks.
std::cin.eof() tests for end-of-file (hence eof), not for errors. For error checking use !std::cin.good(), the built-in conversion operator (if(std::cin)) or the boolean negation operator (if(!std::cin)).
Use a direct test of the status of the stream with:
while (cin >> i)
{
...
}
For an input stream to enter the EOF state you have to actually make an attempt to read past the end of stream. I.e. it is not enough to reach the end-of-stream location in the stream, it is necessary to actually try to read a character past the end. This attempt will result in EOF state being activated, which in turn will make cin.eof() return true.
However, in your case you are not only not doing that, you (most likely) are not even reaching the end of stream. If you input your 10 from the keyboard, you probably finished the input by pressing the [Enter] key. This resulted in a new-line character being added to the input stream. So, what you are actually parsing with >> operator in this case is actually a 10\n sequence. Since you requested an int value from the stream, it only reads the numerical characters from the stream, i.e. it reads 1 and 0, but it stops at \n. That \n remains in the stream. You never read it. So, obviously, your code never reaches the end-of-file position in the stream. You have to reason to expect cin.eof() to become true in such case.
#include <iostream>
int main() {
using namespace std;
int i;
if (cin >> i) {
cout << "Extracted an int, but it is unknown if more input exists.\n";
char c;
if (cin.get(c)) { // Or: cin >> c, depending on how you want to handle whitespace.
cin.putback(c);
cout << "More input exists.\n";
if (c == '\n') { // Doesn't work if you use cin >> c above.
cout << "But this was at the end of this line.\n";
}
}
else {
cout << "No more input exists.\n";
}
}
else {
cout << "Format error.\n";
}
return 0;
}
Also see Testing stream.good() or !stream.eof() reads last line twice.
Sample session with the above program, note that input lines are marked with comments not present in the actual output:
$ your-program
12 # input
Extracted an int, but it is unknown if more input exists.
More input exists.
But this was at the end of this line.
$ your-program
12a # input
Extracted an int, but it is unknown if more input exists.
More input exists.
$ echo -n 12 | your-program
Extracted an int, but it is unknown if more input exists.
No more input exists.
$ your-program
a # input
Format error.
Assuming your input is line based, I suggest that you read the whole line using std::getline(). Once you have the line, you can analyse it and decide whether it contains correct or wrong input. Put the line into std::istringstream and do something like the following:
Edit: Changed !! iss to static_cast<bool>(iss) for compatibility with C++0x.
std::istringstream iss (line);
char ch;
long lval;
// read the input
iss >> lval;
// result variable will contain true if the input was correct and false otherwise
result
// check that we have read a number of at least one digit length
= static_cast<bool>(iss)
// check that we cannot read anything beyond the value read above
&& ! (iss >> ch);
Adding to the previous answer:
After reading your input (like 10), you are not at end-of-file, as you can easily type some more. How is the system to know that you will not?
When reading your second input (12a), it correctly reads all the digits that can be part of an integer. The letter 'a' cannot, so it is left for some possible later input. For example, you can read all parts of 12a with this code
int i;
char c;
cin >> i >> c;
cin.eof() test if the stream has reached end of file which happens if you type something like Ctrl+C (on Windows), or if input has been redirected to a file etc.
To test if the input contains an integer and nothing but an integer, you can get input first into a string and then convert that with a stringstream. A stringstream indeed reaches eof if there's no more to be extracted from it.
#include <iostream>
#include <sstream>
#include <string>
int main() {
using namespace std;
int i;
string input;
cin >> input; //or getline(cin, input)
stringstream ss(input);
if (ss >> i && ss.eof()) { //if conversion succeeds and there's no more to get
cout<< "\n Correct Input \n";
}
else {
cout<< "\n Format Error \n";
}
return 0;
}
This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Closed 4 years ago.
The following C++ code uses a ifstream object to read integers from a text file (which has one number per line) until it hits EOF. Why does it read the integer on the last line twice? How to fix this?
Code:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream iFile("input.txt"); // input.txt has integers, one per line
while (!iFile.eof())
{
int x;
iFile >> x;
cerr << x << endl;
}
return 0;
}
input.txt:
10
20
30
Output:
10
20
30
30
Note: I've skipped all error checking code to keep the code snippet small. The above behaviour is seen on Windows (Visual C++), cygwin (gcc) and Linux (gcc).
Just follow closely the chain of events.
Grab 10
Grab 20
Grab 30
Grab EOF
Look at the second-to-last iteration. You grabbed 30, then carried on to check for EOF. You haven't reached EOF because the EOF mark hasn't been read yet ("binarically" speaking, its conceptual location is just after the 30 line). Therefore you carry on to the next iteration. x is still 30 from previous iteration. Now you read from the stream and you get EOF. x remains 30 and the ios::eofbit is raised. You output to stderr x (which is 30, just like in the previous iteration). Next you check for EOF in the loop condition, and this time you're out of the loop.
Try this:
while (true) {
int x;
iFile >> x;
if( iFile.eof() ) break;
cerr << x << endl;
}
By the way, there is another bug in your code. Did you ever try to run it on an empty file? The behaviour you get is for the exact same reason.
I like this example, which for now, leaves out the check which you could add inside the while block:
ifstream iFile("input.txt"); // input.txt has integers, one per line
int x;
while (iFile >> x)
{
cerr << x << endl;
}
Not sure how safe it is...
There's an alternative approach to this:
#include <iterator>
#include <algorithm>
// ...
copy(istream_iterator<int>(iFile), istream_iterator<int>(),
ostream_iterator<int>(cerr, "\n"));
Without to much modifications of the original code, it could become :
while (!iFile.eof())
{
int x;
iFile >> x;
if (!iFile.eof()) break;
cerr << x << endl;
}
but I prefer the two other solutions above in general.
The EOF pattern needs a prime read to 'bootstrap' the EOF checking process. Consider the empty file will not initially have its EOF set until the first read. The prime read will catch the EOF in this instance and properly skip the loop completely.
What you need to remember here is that you don't get the EOF until the first attempt to read past the available data of the file. Reading the exact amount of data will not flag the EOF.
I should point out if the file was empty your given code would have printed since the EOF will have prevented a value from being set to x on entry into the loop.
0
So add a prime read and move the loop's read to the end:
int x;
iFile >> x; // prime read here
while (!iFile.eof()) {
cerr << x << endl;
iFile >> x;
}
At the end of the last line, you have a new line character, which is not read by >> operator and it is not an end of file.
Please, make an experiment and delete the new line (thelast character in file) - you will not get the duplication.
To have a flexible code and avoid unwanted effects just apply any solution given by other users.
int x;
ifile >> x
while (!iFile.eof())
{
cerr << x << endl;
iFile >> x;
}