简体   繁体   中英

Modern Way of checking if C++ input is integer or not

I want to add the check-in my c++ code that user can not enter not integral values in reg. If he inputs, he is prompted again. I saw the solutions in the stack overflow that was of 2011 ( How to check if input is numeric in C++ ). Is there some modern or good way now or is it same?

I tried using ifdigit() in ctype.h

// Example program
#include <iostream>
#include <ctype.h>
using namespace std;
int main()
{
    int x;
    cout<<"Type X";
    cin>>x;
    if(!isdigit(x))
    {
     cout<<"Type Again";
     cin>>x;
    }
}

but it didnt worked

here is my actual problem where I want to add check.

 cout << "Type Reg # of Student # " << i + 1 << endl;
 do
 {
      cin >> arr[i][j];
 } while (arr[i][j] < 999 || arr[i][j] > 9999);

where i and j are in dec. in for loop. I just want to add check that input is not string or something like this. Cant rely on 2011 answer

Check out the below example.

All the magic happens inside to_num() , which will handle white space before and after the number.

#include <iostream>
#include <sstream>
#include <string>
#include <tuple>

auto to_num(const std::string& s)
{
    std::istringstream is(s);
    int n;
    bool good = (is >> std::ws >> n) && (is >> std::ws).eof();

    return std::make_tuple(n, good);
};

int main()
{
    int n;
    bool good;

    std::cout << "Enter value: ";
    for(;;)
    {
        std::string s;
        std::getline(std::cin, s);

        std::tie(n, good) = to_num(s);
        if(good) break;

        std::cout << s << " is not an integral number" << std::endl;
        std::cout << "Try again: ";
    }
    std::cout << "You've entered: " << n << std::endl;

    return 0;
}

Explanation of what's going on inside to_num() :

  1. (is >> std::ws >> n) extracts (optional) leading white space and an integer from is . In the boolean context is 's operator bool() will kick in and return true if the extraction was successful.

  2. (is >> std::ws).eof() extracts (optional) trailing white space and will return true if there is no garbage at the end.

UPDATE

Here is a slightly cleaner version that uses Structured binding declaration and Class template argument deduction available in c++17:

#include <iostream>
#include <sstream>
#include <string>
#include <tuple>

auto to_num(const std::string& s)
{
    std::istringstream is(s);
    int n;
    bool good = (is >> std::ws >> n) && (is >> std::ws).eof();

    return std::tuple(n, good); // look ma, no make_tuple
};

int main()
{
    std::cout << "Enter value: ";
    for(;;)
    {
        std::string s;
        std::getline(std::cin, s);

        auto [n, good] = to_num(s); // structured binding
        if(good)
        {
            std::cout << "You've entered: " << n << std::endl;
            break;
        }
        else
        {
            std::cout << s << " is not an integral number" << std::endl;
            std::cout << "Try again: ";
        }
    }

    return 0;
}

If you properly handle errors, you'll end up with a prompt / input loop that looks something like this:

#include <iostream>
#include <limits>

int getInput() {

    while (true) {

        std::cout << "Please enter a number between 80 and 85: ";

        int number = 0;
        std::cin >> number;

        std::cout << "\n";

        if (std::cin.eof()) {
            std::cout << "Unexpected end of file.\n";
            std::cin.clear();
            continue;
        }

        if (std::cin.bad() || std::cin.fail()) {
            std::cout << "Invalid input (error reading number).\n";
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            continue;
        }

        if (number < 80 || number > 85) {
            std::cout << "Invalid input (number out of range).\n";
            continue;
        }

        return number;
    }

    // unreachable
    return 0;
}

int main() {

    int number = getInput();

    std::cout << number << std::endl;
}

We can omit the range check if we don't need it.

We handle std::cin.eof() (eg user presses ctrl+Z on Windows) separately from the other conditions, since for eof there's nothing to ignore.

This follows the standard C++ stream behavior for whitespace and number conversion (ie it will accept inputs with extra whitespace, or inputs that only start with numeric values).

If we want more control over what we want to accept, or don't want conversion to depend on the locale, we have to use std::getline to read input from std::cin , and then do the string conversion ourselves (either with std::stringstream as in Innocent Bystander's answer , or using std::strtol , or std::from_chars ).

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