6

I want to write a program so that it takes two sets of integer input from terminal and computes two sums. My intention is to separate the two sets of input by EOF (pressing Ctrl+D). Here is my code:

#include <iostream>
using namespace std;

int main(){
    int i,sum=0;
    while((cin>>i).good())
        sum+=i;
    cout<<"Sum 1 is "<<sum<<endl;
    cin.clear();
    sum=0;
    while((cin>>i).good())
        sum+=i;
    cout<<"Sum 2 is "<<sum<<endl;
    return EXIT_SUCCESS;
}

The compiled program worked fine for the first set of integer inputs. But as soon as I pressed Ctrl+D, the first sum was computed and printed and, without taking any further input, printed the second sum as 0. So basically the second while loop failed at the very beginning, even though cin.iostate had been set to good before it. So why did this happen? How should I change the program so that the second while loop would proceed as intended?

God_of_Thunder
  • 753
  • 3
  • 20
  • 46
  • @KerrekSB OP is resetting the stream into a good state with `std::cin.clear()`, but he needs to clear the buffer with `std::cin.ignore()` too in order to use the stream after an EOF, right? – Manu343726 Dec 28 '13 at 14:00
  • You **not** test for `std::cin.good()` as it will cause the last line to be ignored, e.g., when the last line is missing the end of line termination. Just use `while(std::cin >> I)`. This change won't address your problem, though. – Dietmar Kühl Dec 28 '13 at 14:03
  • 1
    @Manu343726: Ctrl-D will close the standard input and there is no way to recover it. You'll need to prevent the Ctrl-D from closing the stream. – Dietmar Kühl Dec 28 '13 at 14:16

1 Answers1

10

When you use Ctrl-D while the tty is in canonical mode it closed the system level pipe. Whatever you do to std::cin won't restore the stream into a good state. If you insist in using Ctrl-D to signal the end of the sequence (which is an unusual interface and probably best avoided), you'll need to clear the ICANON flag using tcgetattr() and tcsetattr() for the standard input stream (file descriptor 0). You will need to deal with any control characters.

It is probably easier to read up to the first failure, clear() the state and either ignore() the offending character(s) or check that they have a specific value.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I had actually tried to use invalid input (i.e. input a character instead of an integer) to terminate the first loop and the result was the same as pressing Ctrl+D. Is it due to the same cause as Ctrl+D then? – God_of_Thunder Dec 28 '13 at 14:47
  • 3
    @God_of_Thunder: No. When you entered an invalid character, it will cause `std::ios_base::failbit` to be set which gets `clear()`ed. However, the character will sit there until it is consumed. You'll need to get rid of the character, e.g., using `std::cin.ignore()`. ... and if there are multiple [non-whitespace] character you might need to `ignore()` all of them. – Dietmar Kühl Dec 28 '13 at 14:54
  • So clear() merely resets the bits in iostate rather than restoring the input stream? Doesn't sound too helpful after all. – God_of_Thunder Dec 28 '13 at 15:10
  • 1
    @God_of_Thunder: `clear()` has no idea what it would take to recover from a failed input. In fact, you might want to use the characters encountered elsewise, e.g., to confirm that the characters encountered are `end` to indicate the end of the first set of data. – Dietmar Kühl Dec 28 '13 at 15:12