简体   繁体   中英

C++ check if entered value is unsigned integer

I'm trying to get unsigned integer from std::cin.

When enter a char or string my program stucks

#include <iostream>
#include <climits>

using namespace std;

int main()
{
    unsigned int number;
    do
    {
        cout << "Enter a unsigned number\t";
        cin >> number;

        if (0 < number && number < 65535 && std::cin.good())
        {
            cout << "It's a number";
            break;
        }
        cout << "Not a number";
    } while (true);
}

How to do that?

It seems that std::cin needs to be recovered when a failure happens( std::cin.clear() fails to restore input stream in a good state )

Then I think using unsigned int is not a solution to enforce positiveness because c/c++ allow implicit conversion between unsigned int and int , so if you do unsigned int x = -1 , x will become 4294967295(assuming 32 bits int) due to underflow.

#include <iostream>

int main()
{
    int number;
    while (true)
    {
        std::cout << "Enter an unsigned number\n";
        std::cin >> number;

        if (std::cin.fail())
        {
            std::cout << "Please enter a number\n";
            std::cin.clear();       
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        else if (number > 0)
        {
            std::cout << "It's an unsigned number\n";
            break;
        }
        else
        {
            std::cout << "It's an negative number\n";
        }
    }
}

The problem when you enter a non-numeric character is that extraction fails and that the character is still in the input stream. You need to remove it if extraction fails.

Another potential problem is your choice of type for the input, unsigned int . If that happens to be a 32 bit integer (which is very common) and the user enters -4294967295 for example, the extraction will succeed and the number will wrap around and become 1 - which is then accepted by the program. When using formatted extraction of numbers, it's safer to use a signed type (wider than needed for the target range) even if you want an unsigned number - and then check the range.

Example:

#include <cstdint>
#include <iostream>
#include <limits>

int main() {
    std::int32_t number; // enough to hold 0-65535 (and then some)

    do {
        std::cout << "Enter a number (1-65534)\t";

        // check that extraction succeeds and the number is in range [1,65534]:
        if(std::cin >> number && number > 0 && number < 65535) {
            std::cout << number << " is a valid number\n";
            break;
        } else {
            if(std::cin.eof()) {
                std::cout << "Bye bye\n";
                return 1;
            }
            std::cout << "Not a valid number\n";
            // clear error state
            std::cin.clear();

            // ignore the rest of the line
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
    } while(true);

    // if you want to store the number in a type suited for your range:
    auto final_number = static_cast<std::uint16_t>(number);
}

You can use getline() function of string class and achieve it as below:

#include <iostream>
#include <climits>
#include <sstream>

using namespace std;

int main()
{
    string number;
    do
    {
        cout << "Enter an unsigned number\t";

        unsigned int num;
        while (getline(cin, number))
        {
            if(number.at(0) != '-')
            {
                stringstream ss(number);
                if (ss >> num)
                {
                    if (ss.eof())
                    {   // Success as found numbers
                        break;
                    }
                }
                cout << "Error in input!" << endl;
            }
            cout << "Error as -ve number!" << endl;
        }

        cout << "Entered num " << num << endl;
    } while (true);
}

Further you can modify this function as per your need to add constraint for accepting numbers. This solution is to avoid your infinite loop and accepting numbers.

When you input a string value, operator>> tries to extract your string input into variable 'number'. This fails since string cannot be converted into unsigned int. This wrong input value is then left in the buffer, while std::cin fails. Any future inputs for extraction will also fail silently. To get past this use,

#include <limits>
if (std::cin.fail()) // has input extraction failed?
{
    std::cin.clear(); // then clear cin and reset it
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); //clear buffer
}

std::cin.ignore() takes in two parameters - the first one is number of characters to remove and the second is the parameter until which to remove characters from the buffer.

std::numeric_limits<std::streamsize>::max() just gives the largest value that can be stored in type std::streamsize . You can just use a value like 200 or 300, but if the user has somehow input more values, using std::numeric_limits would help guard against this possibility. Add this check in the appropriate place in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM