Infinite loop reading from stdin using cin::fail()

634 views Asked by At

I'm struggling with a vector push_back function. The goal is to have a function which pushes n number of elements until you decide to stop. So my idea of a 'stop', is cin.fail().

The faulty function is

void pushbackVector(vector<double> &data)
{
    double input;
    cin >> input;

    for (int i = 0; i < 100; i++)
    {
        if (cin.fail())
        {
            cout << "Ending input.\n";
            return;
        }
        else
        {
            data.push_back(input);
        }
    }

Problem is that when I attempt to use it, I enter a infinite loop.

I have yet to sort the first vector in ASC order, second in DESC order and concatenate the first and second into the third vector. However I'm confident that I can manage this on my own. Anyway the whole code....

#include<iostream>
#include<vector>
#include<algorithm>
#include<cctype>

using namespace std;

// function prototypes
void menu();
void printVector(const vector<double> &data);
void pushbackVector(vector<double> &data);
void sortVector (vector<double> &data);

int main()
{
    menu();

    vector<double> row1;
    vector<double> row2;

    /* not yet used
    vector<double> row3;
    */

    int input;
    cin >> input;

    bool exit = 0;

    while (!exit)
    {
        switch (input)
        {
            case 1:
                pushbackVector(row1);
                break;

            case 2:
                pushbackVector(row2);
                break;

            case 3:
                printVector(row1);
                break;

            case 4:
                printVector(row2);
                break;

            case 5:
                cout << "Printing out the contents of row 1\n";
                printVector(row1);
                cout << "Printing out the contents of row 2\n";
                printVector(row2);
                cout << "Printing out the contents of row 3\n";
                // printVector(row3);
                break;

            case 6:
                cout << "Exitting\n";
                exit = 1;
                break;

            default:
                cout << "Invalid choice\n";
        }
    }


    return 0;
}

void menu()
{
    cout << "Choose an option\n";
    cout << "1) Enter first vector\n";
    cout << "2) Enter second vector\n";
    cout << "3) Print out the first vector\n";
    cout << "4) Print out the second vector\n";
    cout << "5) Print out all three vectoros\n";
    cout << "6) Exitting the program\n";
}

void printVector(const vector<double> &data)
{
    for(int i = 0; i < data.size(); i++)
    {
        cout << data[i] << ", ";
    }
    cout << "\n";
}

void pushbackVector(vector<double> &data)
{
    double input;
    cin >> input;

    for (int i = 0; i < 100; i++)
    {
        if (cin.fail())
        {
            cout << "Ending input.\n";
            return;
        }
        else
        {
            data.push_back(input);
        }
    }

}

void sortVector (vector<double> &data)
{
    cout << "Sorting your vector \n";
    sort(data.begin(), data.end());
}
6

There are 6 answers

0
Anton Antonov On BEST ANSWER

I was done some days before but I forgot to post my answer. Who knew that I could just say if the cin fails, stop the input but don't end the program, lol.

    #include<iostream>
    #include<vector>
    #include<algorithm> // for sort algorithms
    #include<iomanip>  // for setprecision

    using namespace std;

    // function prototypes
    void menu();
    void printVector(const vector<double> &data);
    void pushbackVector(vector<double> &data);
    void sortVector (vector<double> &data);

    int main()
    {
        vector<double> row1;
        vector<double> row2;
        vector<double> row3;

        int input;
        bool exit = false;

        while(!exit)
        {
            menu();
            cin >> input;

            switch (input)
            {
                case 1:
                    cout << "Entering vector 1\n";
                    pushbackVector(row1);
                    sortVector(row1);

                    cout << "\n";
                    break;

                case 2:
                    cout << "Entering vector 2\n";
                    pushbackVector(row2);
                    sortVector(row2);
                    reverse(row2.begin(), row2.end());

                    cout << "\n";
                    break;

                case 3:
                    cout << "Printing out vector 1\n";
                    printVector(row1);

                    cout << "\n";
                    break;

                case 4:
                    cout << "Printing out vector 2\n";
                    printVector(row2);

                    cout << "\n";
                    break;

                case 5:
                    // reserve enough space for all of row1's and row2's elements
                    row3.reserve(row1.size() + row2.size());
                    // insert row1's elements at the end of row3
                    row3.insert(row3.end(), row1.begin(), row1.end());
                    // insert row2's elements at the end of row3
                    row3.insert(row3.end(), row2.begin(), row2.end());


                    cout << "Printing out the contents of vector 1\n";
                    printVector(row1);
                    cout << "Printing out the contents of vector 2\n";
                    printVector(row2);
                    cout << "Printing out the contents of vector 3\n";
                    printVector(row3);


                    cout << "\n";
                    break;

                case 6:
                    cout << "Exitting\n";
                    exit = true;
                    break;

                default:
                    cout << "Invalid choice\n";
            }
        }


        return 0;
    }

    void menu()
    {
        cout << "Choose an option\n";
        cout << "1) Enter first vector\n";
        cout << "2) Enter second vector\n";
        cout << "3) Print out the first vector\n";
        cout << "4) Print out the second vector\n";
        cout << "5) Print out all three vectoros\n";
        cout << "6) Exitting the program\n";
    }


    void printVector(const vector<double> &data)
    {
        for(int i = 0; i < data.size(); i++)
        {
            cout << setprecision(4) << data[i] << " ";
        }
        cout << "\n";
    }

    void pushbackVector(vector<double> &data)
    {
        double input;
        int numOfItems;
        cout << "How many items you want to add into vector?: ";
        cin >> numOfItems;

        for (int i = 0; i < numOfItems; i++)
        {
            cin >> input;
            if (cin.fail())
            {
                cout << "Ending input.\n";
                return;
            }
            else
            {
                data.push_back(input);
            }
        }
    }

    void sortVector(vector<double> &data)
    {
        cout << "Sorting your vector \n";
        sort(data.begin(), data.end());
    }
5
jrok On

You're only reading once, move the read inside the loop:

void pushbackVector(vector<double> &data)
{
    double input;
    // cin >> input;   --------------
                                    //
    for (int i = 0; i < 100; i++)   //
    {                               //
        cin >> input;   // <---------

        if (cin.fail())
        {
            cout << "Ending input.\n";
            return;
        }
        else
        {
            data.push_back(input);
        }
    }

That'll make sure that you actually get input. Now, if you're not going to enter 100 values, you need to somehow notify the stream. That's done by insert "EOF character" in it. Press CTRL+Z on windows or CTRL+D on unix terminals.

When this gets read from the stream, it enters a fail (and eof) state and it'll stay like that unless you clear the error flags by calling cin.clear() when appropriate.

You've made the same mistake also in main. You only read once before the while loop, so input keeps the value you initialy entered and keeps entering the same choice. I think that's the infinite loop you're talking about. To fix it, move the read statement just before the switch.

Hope that helps.


Also, this is how I'd write the function:

double input;
for (int i = 0; (cin >> input) && i < 100; ++i) {
     data.push_back(input);
}
cout << "Ending input.\n";

Streams can be used in boolean expression - they convert to the result of !fail() - and such are a convenient and idiomatic way to control a loop.

0
Shoe On

The infinite loop is cause by the fact that you are reading:

cin >> input;

once and then entering a while loop (in your main) that will keep going forever (unless the input is initially equal to 6).

Change:

cin >> input;

bool exit = 0;

while (!exit)
{
    // ...

to:

bool exit = 0;

while (!exit)
{
    cin >> input;
    // ...

Depending on what your logic is, the same is happening in the pushbackVector function at:

double input;
cin >> input;

for (int i = 0; i < 100; i++)
{
    // ...

You might want to change that to:

double input;

for (int i = 0; i < 100; i++)
{
    cin >> input;
    // ...
1
Tony The Lion On

Do something like this instead:

void pushbackVector(vector<double> &data)
{
    double input;

    while (cin >> input) //will return true when there's valid input, otherwise false
    {
        if (input == -1)
        {
            cout << "Ending input.\n";
            return;
        }
        else
        {
            data.push_back(input);
        }
    }

This will read the input until you enter -1 or enter invalid input.

I think relying on cin.fail() is just not a good idea. The proper way to use fail() is explained in the link. It is not what you may expect.

0
Davidbrcz On

Putting aside the reading which is misplaced, if you enter something you should not, you need to purge the input.

Add in the block of te first if

cin.clear(); 
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
0
Vlad from Moscow On

I would write the function the following way

void pushbackVector( std::vector<double> &data )
{
    cin.clear(); 
    cin.ignore( std::numeric_limits<streamsize>::max(), '\n' );

    data.insert( data.end(), std::istream_iterator<double>( std::cin ), std::istream_iterator<double>() ); 
}