Rose
Rose

Reputation: 81

How do I successfully clear streams?

I'm writing a program that needs to find abundant, deficient and perfect numbers - according to Greek numerology. What represents an issue here is ensuring that only natural numbers are inserted; more specifically making sure that there are no letters inserted. I tried checking if stream was "broken", if true, print appropriate message. It works, but only once, and I can't figure out why. Here's the code.

 #include <iostream>
 int main ()
{
double n(0);
int sum(0);
int i(0);

std::cout << "Enter a natural number or 0 for exit: ";
std::cin >> n;

int n1 = n;
do {
    for(i = 1; i < n1; i++) {
        if(n1 % i == 0) {
            sum+=i;
        }
    }
    if(!std::cin) {
        std::cout << "Not a natural number!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(n != int(n)) {
        std::cout << "Not a natural number!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(n1 < 0) {
        std::cout << "Not a natural number!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(suma < n1) {
        std::cout << "Number " << n << " is deficient!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(suma > n1) {
        std::cout << "Number " << n << " is abudant!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(suma == n1) {
        std::cout << "Number " << n << " is perfect!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    suma = 0;
    std::cin.clear();
    std::cin.ignore(1000, '\n');
    std::cin >> n;
    n1 = int(n);
} while(n != 0);
std::cout << "Goodbye!";

return 0;
}

Upvotes: 0

Views: 108

Answers (2)

Rose
Rose

Reputation: 81

So the only problem was assignment of 0 to n1. When the stream "gets broken", that is it receives input that doesn't corresponds to the variable type, it get's to an undefined state, which has to be cleared with .clear and .ignore, which again, leaves it hanging with 0 value, resulting in ending the loop. My solution is perhaps the most primitive one out there. Just assign a random integer value to n1, and case closed. It will not affect other ifs, as only the first one will be executed, and it will also prevent loop from breaking. So just: if(!(std::cin >> n)) { std::cout << "Not a natural number!" << std::endl; std::cin.clear(); std::cin.ignore(1000, '\n'); n = 1; }

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84559

The problem appears to be one of logic. You prompt for reinput of n, then

std::cin.clear();
std::cin.ignore(1000, '\n');
std::cin >> n;

That will not "recover" the stream, that will discard the user input of 'n' leaving you stuck. It appears you need to reorder your input and loop to handle the input once per iteration. Something similar to the following:

#include <iostream>

int main (void) {

    double n(0);

    do {
        int sum(0);
        int i(0);

        std::cout << "Enter a natural number or 0 for exit: ";
        std::cin >> n;
        int n1 = n;

        for(i = 1; i < n1; i++)
            if(n1 % i == 0)
                sum+=i;

        if(n != int(n))
            std::cout << "Not a natural number!" << std::endl;
        else if(n1 < 0)
            std::cout << "Not a natural number!" << std::endl;
        else if(sum < n1)
            std::cout << "Number " << n << " is deficient!" << std::endl;
        else if(sum > n1)
            std::cout << "Number " << n << " is abudant!" << std::endl;
        else if(sum == n1)
            std::cout << "Number " << n << " is perfect!" << std::endl;

        sum = 0;

    } while(n != 0);

    std::cout << "Goodbye!\n";

    return 0;
}

Neither .ignore or .clear are necessary. Leading whitespace is ignored for numeric conversion and .clear simply sets 'goodbit' on the stream which has the effect of clearing all error flags. (see std::basic_ios::clear) There is also no real need to check if (!std::cin). It doesn't hurt, it is just superfluous to your code.

Example Use/Output

$ ./bin/inputnumber
Enter a natural number or 0 for exit: 128
Number 128 is deficient!
Enter a natural number or 0 for exit: 30
Number 30 is abudant!
Enter a natural number or 0 for exit: 37
Number 37 is deficient!
Enter a natural number or 0 for exit: 2
Number 2 is deficient!
Enter a natural number or 0 for exit: 1
Number 1 is deficient!
Enter a natural number or 0 for exit: 10
Number 10 is deficient!
Enter a natural number or 0 for exit: 12
Number 12 is abudant!
Enter a natural number or 0 for exit: 0
Number 0 is perfect!
Goodbye!

I'm not 100% clear on your goal, so if I misunderstood, please drop a comment and I'm happy to help further.


Making Your Input Discard Non-Numbers

Rose, after the discussions in the comments, it appears your issue was with n1 being assigned 0 in the event there was an input error (like a cat stepping on the keyboard resulting in ";sdkfgj" being input). That too can be handled, and you were on the right track looking at both .clear and .ignore. Hereto, there is a bit of logic in how you put that together.

When faced with the problem of "How do I make sure the user only enters 'X'?", the normal approach is to loop continually until the user input satisfies the conditions you have set for accepting input. Here, you want a numeric value input. So the approach would be to loop, prompting for input, and then checking the stream state after the user inputs a value. If the stream is in error, you need to .clear() the error, and then .ignore(..., ...) up to the '\n'. (you can use the values in <limits> to disable the character count check and discard whatever number of characters may be present)

This leaves a slight problem. You are looping for input within a loop to begin with. If the user enters 0 to indicate here would like to exit, then you must break both loops to get to "Goodbye!\n";. While you can set flags, etc., the lowly goto is still the standard, and efficient, way to jump out of nested loops.

Putting it altogether, now understanding a bit more about your problem, you could do something similar to the following:

#include <iostream>
#include <limits>

int main (void) {

    double n(0);

    do {
        int sum(0), n1(0);

        while (n1 == 0) {
            std::cout << "Enter a natural number or 0 for exit: ";
            std::cin >> n;
            if (!std::cin) {
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
                                '\n');
            }
            else if (n == 0)
                goto alldone;
            else
                n1 = n;
        }

        for(int i = 1; i < n1; i++)
            if (n1 % i == 0)
                sum += i;

        if(n != int(n))
            std::cout << "Not a natural number!" << std::endl;
        else if(n1 < 0)
            std::cout << "Not a natural number!" << std::endl;
        else if(sum < n1)
            std::cout << "Number " << n << " is deficient!" << std::endl;
        else if(sum > n1)
            std::cout << "Number " << n << " is abudant!" << std::endl;
        else if(n1 && sum == n1)
            std::cout << "Number " << n << " is perfect!" << std::endl;

        sum = 0;

    } while(n != 0);

    alldone:;
    std::cout << "Goodbye!\n";

    return 0;
}

Example Use/Output

$ ./bin/inputnumber
Enter a natural number or 0 for exit: apples
Enter a natural number or 0 for exit: banannas
Enter a natural number or 0 for exit: 128
Number 128 is deficient!
Enter a natural number or 0 for exit: 96
Number 96 is abudant!
Enter a natural number or 0 for exit: foo
Enter a natural number or 0 for exit: 2
Number 2 is deficient!
Enter a natural number or 0 for exit: 0
Goodbye!

Let me know if that is the area that was giving your problems.

Upvotes: 1

Related Questions