2

This is my first post on here, and I am very new to programming/C++ (literally just a couple of weeks into this rabbit hole). I have this test project up in Microsoft Visual Studio 2017, and I am trying to figure out how to completely foolproof user input while using cin. If I am asking for an int, and I only want a 1 or a 0, I want there to be absolutely no way for someone to place in something like a 2, or an n, or a space between an incorrect and correct response like "n 1". Currently, I have gotten to the point in which the only way I can seem to create an undesired result is if I enter a correct integer first (0 or 1) and then follow it up with a space and any other character, and this pattern can be followed with an indefinite number of spaces and characters after the initial correct one (0 a 42 f 9130) etc. Besides just getting the desired result with even more messy code, I'm wondering if I'm just missing some in-built functions that I have not heard of yet in which can make this process much more efficient. Here is what I wrote to get to this point:

#include <iostream>
#include <string>
#include <climits>

using namespace std;

int trap;
int wrongNumber();

int numOnly() {
    while (cin.fail())
    {
        // Using someone else's code for this while statement to figure out how to not take a char input when using an int
        // Update: Turned this into a function to be called on whenever cin has a chance to fail because users don't listen.

        cin.clear(); // clear input buffer to restore cin to a usable state
        cin.ignore(INT_MAX, '\n'); // ignore last input
        system("CLS");
        cout << "----------------------------------" << endl;
        cout << "|  You can only enter a number.  |" << endl;
        cout << "| Would you like to pick a card? |" << endl;
        cout << "|  Type 1 for yes or 0 for no!   |" << endl;
        cout << "----------------------------------" << endl;
        cin >> trap;
    }
    if (trap != 1 && trap != 0) {
        system("CLS");
        wrongNumber();
    }
    return trap;
}

int wrongNumber() {

    // At least I made this fail-safe on my own!

    while (trap != 1 && trap != 0) {
        system("CLS");
        cout << "----------------------------------" << endl;
        cout << "|    That is not a 1 or a 0!     |" << endl;
        cout << "| Would you like to pick a card? |" << endl;
        cout << "|   Type 1 for yes or 0 for no!  |" << endl;
        cout << "----------------------------------" << endl;
        cin >> trap;
    }
    if (cin.fail()) {
        system("CLS");
        numOnly();
    }
    return trap;
}

int main() {

    cout << "----------------------------------" << endl;
    cout << "| Would you like to pick a card? |" << endl;
    cout << "|   Type 1 for yes or 0 for no!  |" << endl;
    cout << "----------------------------------" << endl;
    cin >> trap;

    while (cin.fail())
    {
        numOnly();
    }

    if (trap != 1 && trap != 0) {
        wrongNumber();
    }
Entropy
  • 23
  • 4
  • 2
    You really can't *stop* someone from entering invalid input with `std::cin`. However, you could read the input into a `std::string`, then validate it after the user enters whatever. If it's not a '1' or '0', print an error and ask again. Otherwise, you might want to check out [ncurses](https://en.wikipedia.org/wiki/Ncurses) or some other way to get input. – Steve Sep 12 '18 at 00:48

5 Answers5

1

I recommend you do not use an integer to store a "Yes" or "No" answer and use a string instead. This way you can save yourself some lines of code withcin.fail() , cin.ignore() and cin.clear():

 int main() {

    string trap;
    cout << "----------------------------------" << endl;
    cout << "| Would you like to pick a card? |" << endl;
    cout << "|   Type 1 for yes or 0 for no!  |" << endl;
    cout << "----------------------------------" << endl;
        cin>>trap;

        while (trap != "1" && trap != "0") { //You can make this while block into a function if you prefer
            cout << "----------------------------------" << endl;
            cout << "|    That is not a 1 or a 0!     |" << endl;
            cout << "| Would you like to pick a card? |" << endl;
            cout << "|   Type 1 for yes or 0 for no!  |" << endl;
            cout << "----------------------------------" << endl;
                cin>>trap;
        }
    return 0;
}

If you must use an integer then you should look at this and capturing characters without pressing enter.

GKE
  • 960
  • 8
  • 21
0

The best one could do without relying on some OS functions:

#include <iostream>
#include <limits> // std::numeric_limits
#include <cctype> // std::isspace

// function to read and discard whitespace except '\n' from a stream
std::istream& eat_whitespace(std::istream &is)
{
    char ch;

    // as long as the next character in the stream is a space and not a newline
    while (std::isspace(ch = is.peek()) && ch != '\n') 
        is.get(); // get and discard the character

    return is;
}

int read_range_strict(std::istream &is, int min, int max)
{
    int value;

    // as long as
    while (!(is >> value >> eat_whitespace) // extraction of an int fails
           || is.peek() != '\n' // or the next character in the stream is not a newline *)
           || value < min || max < value // or the value is not within the specified range
    ) {

        std::cerr << "Please enter a number between " << min << " and " << max << "!\n\n";
        is.clear(); // clear flags
        // discard everything that might be left in the stream
        is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }

    return value;
}

int main()
{
    int value;
    do {
        value = read_range_strict(std::cin, 0, 1);
    } while (true); // only for demo
}

*) which we tried to establish using eat_whitespace. So if there is anything left in the stream that isn't a \n we got some garbage after the number.

Swordfish
  • 12,971
  • 3
  • 21
  • 43
0

Since the only valid values are 0 & 1 you don't need to read the input as an int. Just read it as a string, trim any whitespace and compare your string to "0" or "1".

Of course you could also just accept "y" or "n" instead which would be a bit more user friendly.

ADG
  • 343
  • 1
  • 9
0

As one of the answers state, it is easier to read the input into a string and then evaluate that string. Here is an example:

#include <iostream>
#include <string>

int main() {
  std::string trap;

  std::cout << "Enter 1 or 0" << std::endl;
  std::getline(std::cin, trap); // fetch user input, save into trap
  while (std::cin.fail() || (trap != "1" && trap != "0")) {
    std::cout << "That was not a 1 or 0; try again" << std::endl;
    std::getline(std::cin, trap);
  }
  return 0;
}

This code reads all user input, determines if it is a 1 or 0, and either exits successfully or prompts the user based on what they entered.

If I understand correctly, something like this may help you achieve what you want in terms of foolproofing user input. I recommend you adapt your code to something similar instead of having several functions which perform similar tasks.

Axel
  • 41
  • 3
0

Depending on your compiler you could use C code like getch() and after doing your checks only then echo it to the screen. You'd then have to get the code char by char and obviously assemble your string.

https://www.c-lang.thiyagaraaj.com/archive/c-blog/use-of-getch-getche-and-getchar-in-c

Disclaimer: Not all C++ compilers may support this. This is C code and not C++.

solarflare
  • 423
  • 3
  • 14