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.
I am compiling the following code under Microsoft Visual Studio 2013
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream in("filename.txt");
int n;
while (!in.eof()) {
in >> n;
cout << n << endl;
}
return 0;
}
filename.txt contains:
4
note: the second line is empty line.
I am getting this output:
4
4
My question is: Why 4 is duplicated in the output when I add trailing spaces or empty lines to the end of the file? and why eof is not working properly in this case?
Using !eof(), !fail(), !bad(), or !good(), is invariably the wrong way to condition your input. The point is to perform the extraction first, and then check if succeeded. If you do the former, you'll almost always get the problems you are experiencing.
The first extraction into n obtained 4 from the stream. After it found the newline it stopped and you printed the value of n. When the second loop executed, the extractor cleared the leading whitespace from the stream (meaning the newline after 4), found the end of the stream and set eofbit. Since no characters were extracted, it also set failbit. When you attempted to print the value again, all you were seeing was the result of the previous input operation.
This isn't the right way to perform a read. You have to do it before checking the stream. This is how it is commonly done:
while (in >> n)
{
std::cout << n << std::endl;
}
After the read is performed, operator bool() will be invoked on the stream, which will access its stream state and determine if the extraction succeeded. If it did, only then will the loop body execute.
You aren't checking the state of the operation. Even if in >> n; sets a status of fail (so n was not set to a new value), you perform the next line. See http://www.cplusplus.com/doc/tutorial/files/ or http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/ or similar. Your file has an empty line before the eof, so you run the code in the while loop twice.
Here's what happens during the above code execution:
1st loop iteration:
read 4 into n
output n (4)
2nd loop iteration:
read EOF
(here there is no test for EOF)
output n (4)
3rd loop iteration (doesn't happen, because EOF was read during the last read)
You should be testing the state of in in your loop condition:
while (in >> n)
{
// all is good, carry on
}
Related
Note: I know the solution that would solve the issue, but I don't understand the "computer logic," or what is going on behind the compiler.
In my C++ textbook, there is a blurb of example code that reads:
cout << "Enter a line of input:\n";
char next;
while ((!isdigit (next)) && (next != '\n'))
{
cin.get (next);
cout << next;
}
cout << "<END OF OUTPUT>";
... Paired with the example input: I'll see you at 10:30 AM.
I would expect, after typing in the input, for the output to be I'll see you at <END OF OUTPUT>. Instead, the first digit, "1," is also output, such that it becomes I'll see you at 1<END OF OUTPUT>. Why is this? Is this because the statement cin.get (next); is not declared/initialized outside of the while loop, and thus the first next value tested in the while loop's conditional parameters is not actually the first character of the keyboard input? Or does the while loop run an extra iteration when the next condition is not satisfied?
If it's the former, what does the computer test if next is not set to a value? And why does the first digit ("1") read still meet the condition to run the loop again one more time before terminating?
As mentioned in the comments, you have to initialize next before using it, otherwise it is undefined behavior.
Now lets assume, next is properly initialized to a non-digit character. The condition (!isdigit (next)) && (next != '\n') is checked once when you enter the while loop and every time when you reach the end of the statement in curly braces. In your first version, you get a new char and immediately stream it to cout. The check is done afterwards and the loop terminates as expected.
Consider this small piece of code:
#include <iostream>
#include<stdio.h>
using namespace std;
int main() {
int a;
while(true){
cin>>a;
cout<<a;
}
return 0;
}
Input
1 2 3 5 7 23
Output
125723
How I thought it will run is:
First iteration
1. Reads the first input ie '1' and stops reading further, right after reading the whitespace.
2.Prints the value 1.
Second iteration
1. Again asks for new input
2. Print that in the second line
But that doesn't happen instead it reads the elements we gave after space
First iteration:
Peek at next character in the stream. It's a digit ('1'), so read it.
Peek at next character in the stream. It's not a digit (' '), so don't read it; store 1 in a and return from >>.
(Output 1.)
Second iteration:
Peek at next character in the stream. It's whitespace (' '), so read and ignore it.
Peek at next character in the stream. It's a digit ('2'), so read it.
Peek at next character in the stream. It's not a digit (' '), so don't read it; store 2 in a and return from >>.
(Output 2.)
And so on ...
The point is that >> does not care about lines. cin is one long input stream of characters (some of which may be '\n'). The only thing you can do is read more characters (and then maybe decide that you don't want to do anything with them).
cin is not necessarily connected to a keyboard. The program that started you gets to decide where cin reads from. It can be a file, a network socket, or interactive user input. In the latter case, reading from cin may block until the user types more input, but it will never cause input to just be dropped.
If you want a sane user interface, always read whole lines and process them afterwards:
std::string line;
while (std::getline(std::cin, line)) {
// do stuff with line
}
This question already has answers here:
Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?
(5 answers)
Why is “while( !feof(file) )” always wrong?
(5 answers)
Closed 7 years ago.
I have a file containing only one character 1 (there is no newline symbol after it)
Reading the contents with this method:
int value;
ifstream in("somefile");
for (!in.eof())
{
in >> value;
cout << "input: " << value << " , eof" << in.eof() << "'\n";
}
gives me the following output:
input: 1 , eof: 0
input: 1 , eof: 1
The questions are:
1) Why EOF flag is not set after first reading try? I mean, if program successfully reads the number on the first try, it somehow knows that the string representatiion of it is over. To find it out it has to try read at least one byte after 1 character and there it should have hitted EOF. Why that doesn' happen?
2) That said, if I have a file with one value per line, I always do have a duplicate of last input. Does it mean that I always have to discard it in any way and that would be correct? Or for example add extra check for EOF after in >> value; and only if it succeeds, do any logic I want?
I know that I can work around with readline() or while(in >> value) methods but it's more a question of understanding what really happens there.
Thank you.
I have a question about the difference between these two pieces of code:
char buffer5[5];
cin.get(buffer5, 5);
cout << buffer5;
cin.get(buffer5, 5);
cout << buffer5;
and
char buffer4;
while (cin.get(buffer4))
{
cout << buffer4;
}
In the first piece of code, the code gets 5 characters and puts it in buffer5. However, because you press enter, a newline character isn't put into the stream when calling get(), so the program will terminate and will not ask you for another round of 5 characters.
In the second piece of code, cin.get() waits for input to the input stream, so the loop doesn't just terminate (I think). Lets say I input "Apple" into the input stream. This will put 5 characters into the input stream, and the loop will print all characters to the output. However, unlike the first piece of code, it does not stop, even after two inputs as I can continuously keep inputting.
Why is it that I can continuously input character sequences into the terminal in the second piece of code and not the first?
First off, "pressing enter" has no special meaning to the IOStreams beyond entering a newline character (\n) into the input sequence (note, when using text streams the platform specific end of line sequences are transformed into a single newline character). When entering data on a console, the data is normally line buffered by the console and only forwarded to the program when pressing enter (typically this can be turned off but the details of this are platform specific and irrelevant to this question anyway).
With this out of the way lets turn our attention to the behavior of s.get(buffer, n) for an std::istream s and a pointer to an array of at least n characters buffer. The description of what this does is quite trivial: it calls s.get(buffer, n, s.widen('\n')). Since we are talking about std::istream and you probably haven't changed the std::locale we can assume that s.widen('\n') just returns '\n', i.e., the call is equivalent to s.get(buffer, n, '\n') where '\n' is called a delimiter and the question becomes what this function does.
Well, this function extracts up to m = 0 < n? n - 1: 0 characters, stopping when either m is reached or when the next character is identical to the delimiter which is left in the stream (you'd used std::istream::getline() if you'd wanted the delimiter to be extracted). Any extracted character is stored in the corresponding location of buffer and if 0 < n a null character is stored into location buffer[n - 1]. In case, if no character is extracted std::ios_base::failbit is set.
OK, with this we should have all ingredients to the riddle in place: When you entered at least one character but less than 5 characters the first call to get() succeeded and left the newline character as next character in the buffer. The next attempt to get() more characters immediately found the delimiter, stored no character, and indicated failure by setting std::ios_base::failbit. It is easy to verify this theory:
#include <iostream>
int main()
{
char buffer[5];
for (int count(0); std::cin; ++count) {
if (std::cin.get(buffer, 5)) {
std::cout << "get[" << count << "]='" << buffer << "'\n";
}
else {
std::cout << "get[" << count << "] failed\n";
}
}
}
If you enter no character, the first call to std::cin.get() fails. If you enter 1 to 4 characters, the first call succeeds but the second one fails. If you enter more than 4 characters, the second call also succeeds, etc. There are several ways to deal with the potentially stuck newline character:
Just use std::istream::getline() which behaves the same as std::istream::get() but also extracts the delimiter if this is why it stopped reading. This may chop one line into multiple reads, however, which may or may not be desired.
To avoid the limitation of a fixed line length, you could use std::getline() together with an std::string (i.e., std::getline(std::cin, string)).
After a successful get() you could check if the next character is a newline using std::istream::peek() and std::istream::ignore() it when necessary.
Which of these approaches meets your needs depends on what you are trying to achieve.
To simplify, I'm trying to read the content of a CSV-file using the ifstream class and its getline() member function. Here is this CSV-file:
1,2,3
4,5,6
And the code:
#include <iostream>
#include <typeinfo>
#include <fstream>
using namespace std;
int main() {
char csvLoc[] = "/the_CSV_file_localization/";
ifstream csvFile;
csvFile.open(csvLoc, ifstream::in);
char pStock[5]; //we use a 5-char array just to get rid of unexpected
//size problems, even though each number is of size 1
int i =1; //this will be helpful for the diagnostic
while(csvFile.eof() == 0) {
csvFile.getline(pStock,5,',');
cout << "Iteration number " << i << endl;
cout << *pStock<<endl;
i++;
}
return 0;
}
I'm expecting all the numbers to be read, since getline is suppose to take what is written since the last reading, and to stop when encountering ',' or '\n'.
But it appears that it reads everything well, EXCEPT '4', i.e. the first number of the second line (cf. console):
Iteration number 1
1
Iteration number 2
2
Iteration number 3
3
Iteration number 4
5
Iteration number 5
6
Thus my question: what makes this '4' after (I guess) the '\n' so specific that getline doesn't even try to take it into account ?
(Thank you !)
You are reading comma separated values so in sequence you read: 1, 2, 3\n4, 5, 6.
You then print the first character of the array each time: i.e. 1, 2, 3, 5, 6.
What were you expecting?
Incidentally, your check for eof is in the wrong place. You should check whether the getline call succeeds. In your particular case it doesn't currently make a difference because getline reads something and triggers EOF all in one action but in general it might fail without reading anything and your current loop would still process pStock as if it had been repopulated successfully.
More generally something like this would be better:
while (csvFile.getline(pStock,5,',')) {
cout << "Iteration number " << i << endl;
cout << *pStock<<endl;
i++;
}
AFAIK if you use the terminator parameter, getline() reads until it finds the delimiter. Which means that in your case, it has read
3\n4
into the array pSock, but you only print the first character, so you get 3 only.
the problem with your code is that getline, when a delimiter is specified, ',' in your case, uses it and ignores the default delimiter '\n'. If you want to scan that file, you can use a tokenization function.