How do I check if a user inputs the wrong value?

Hello, I'm new to C++. I've learned the very basics of C++ and want to start working on more complicated codes.

How do I update the code so that if the user enters an invalid day, month or year it asks the same questions again?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace std;

int main()
{
    int birthMonth=0, birthDay=0, birthYear=0, userAge =0;

    cout << "Month? ";
    cin >> birthMonth;

    cout << "Day? ";
    cin >> birthDay;

    cout << "Year? ";
    cin >> birthYear;
}


Thank you in advance!
It's rather involved, if you want it bomb proof
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <limits>
using namespace std;

int main()
{
  int Month;
  bool ok;
  do {
    cout << "Month? ";
    if ( cin >> Month ) {
      ok = Month >= 1 && Month <= 12;
    }
    // https://en.cppreference.com/w/cpp/io/basic_istream/ignore
    else if (cin.eof() || cin.bad()) {
      break;
    } else if (cin.fail()) {
      cin.clear(); // unset failbit
      cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip bad input
    }
  } while ( !ok );

  if ( ok ) {
    cout << "Your month=" << Month << endl;
  } else {
    cout << "User bailed" << endl;
  }
}



$ ./a.out 
Month? 33
Month? March
Month? Banana
Month? 4
Your month=4


User presses ctrl-d/ctrl-z for end of file

$ ./a.out 
Month? User bailed


Feeding in a binary file as standard input

$ ./a.out < a.out 
Month? Month? Month? Month? Month? Month? Month? Month? Month? Your month=2

It moans a lot, but it shouldn't crash with badly mangled input.

I appreciate the help but there are certain functions that you brought up that I still don't understand. For example, bool, break, cin.clear, cin.ignore and std::. I'm still trying to get used to the basic functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        cout << "Month: ";
        cin >> birthMonth;
	if (birthMonth < 1 || birthMonth > 12)
	{
    	cout << "Invalid answer! Try again!" << endl;
	}
	else
	{
		cout << "Day: ";
    	cin >> birthDay;
		if (birthDay < 1 || birthDay > 31)
		{
			cout << "Invalid answer! Try again!" << endl;
		}
		else
		{
			cout << "Year: ";
    		cin >> birthYear;
			if (birthYear < 1900 || birthYear > 2020)
			{
				cout << "Invalid answer! Try again!" << endl;
			}	
		}
	}


After noticing the use of if statements in your code. This is what I ended up putting together after doing some research on if statements. I just can't figure out how to ask the user the same prompt if they enter an invalid answer. Would I use some sort of loop? Thank you for the help again.
Hello SweatyCoder,

"bool" is a variable type like "chsr", "short", "int" and "double". The difference is that a "bool" is only 1 byte to hold a (0) zero meaning "false" or a (1) meaning "true". This is a vary handy variable type that you will be using more in the future.

"break" is a key work and as the name implies it is used to break out of something. Generally used with for loops, do.while loops, while loops and the switch. The switch you will get to in the future.

The "cin.clear()" is a function of "cin", maybe not the most accurate, but will work for now, and is used to clear the state bits on the stream. http://www.cplusplus.com/reference/ios/ios/clear/

The idea of "cin.ignore(...)" is to clear the input buffer of whatever may be left there. There are several ways to write this: As salem c has used this would be considered the most portable way that any compiler can use. Another way, most often seen, is cin.ignore(1000, '\n');. The first parameter is just a large number. The second parameter, the "\n", is what the ignore would clear to. That is the "ignore" will clear from the input 1000 characters or to the "\n" whichever comes first.

The combination of ".clear()" and ".ignore(...)" is usually used after formatted input. An example cin >> num; where "num" is defined as an int although it could be any numeric type. As formatted input the "cin" expects a numeric value to be typed in. If not "cin" will fail and be unusable the rest of the program unless you use "cin.clear();".

The down side to formatted input is that it will leave the "\n" in the input buffer which could cause a problem especially if you use "std::getline()" for unformatted input. The "getline" will extract up to and including the "\n", but discards the "\n" leaving the input buffer empty.

This is untested. I will work on that part shortly and post any changes I may need to make.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
do
{
	cout << "Month: ";
	cin >> birthMonth;

	if (birthMonth < 1 || birthMonth > 12)
		std::cout << "\n    Invalid Month! Try again.\n";

} while (birthMonth < 1 || birthMonth > 12);


do
{
	cout << "Day: ";
	cin >> birthDay;

	if (birthDay < 1 || birthDay > 31)
		std::cout << "\n    Invalid Day! Try again.\n";

} while (birthDay < 1 || birthDay > 31);

do
{
	cout << "Year: ";
	cin >> birthYear;

	if (birthYear < 1900 || birthYear > 2020)
		cout << "\n    Invalid Year! Try again!\n";

} while (birthYear < 1900 || birthYear > 2020);

This will keep each section in in the loop until you enter valid information. Not as fancy as what salem c posted, but I hope it will help you understand what he did.

Andy
Thanks for the help and for explaining everything to me. I put your code in my compiler and it worked. However, as I'm still new to C++ I've only briefly worked with while and for loops. So seeing do while loops is a new thing for me. Can you explain to me how this works? and if I was to convert this into a while loop what would it look like? I tried but it just infinitely loops "Invalid Answer!" a bunch of times.
Last edited on
Do-while is the same thing as a while loop, except it always enters the loop at least once. After you're in the loop, it only continues looping if the while (condition) is true, just like a normal while loop.

1
2
3
4
5
6
do
{
    x;
    y;
}
while (z);


is equivalent to:
1
2
3
4
5
6
7
x;
y;
while (z)
{
    x;
    y;
}


If you are entering non-numbers e.g. "asdf" as input, then the stream will be set to a failure state, and you'd have to do the cin.clear + ignore trick that salem c did.
Last edited on
Hello SweatyCoder,

I had to step away from the computer earlier and lost track of where I was at. To explain "std::" a bit:

This is known as a name space qualifier. Most classes and books that I have heard about tend to put off teaching about name space till later and think that using namespace std; is a good thing to promote until they get around name spaces. using namespace std; will work for awhile, but eventually will become a problem. Most of your first programs putting the using statement in the program is not going to cause any problem, but at the same time it is not helping you either.

After creating a program to work wit I came up with this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
// <--- Most common includes to start with.
#include <iostream>
#include <iomanip>
#include <limits>

//using namespace std;  // <--- Best not to use.
// The most recent post that is worth reading. http://www.cplusplus.com/forum/beginner/258335/

// <--- A better alternative to using namespace std;
//using std::cin;
//using std::cout;
//using std::endl;
//using std::string;

int main()
{
        constexpr int MAX_DAYS{31};

	int birthMonth{};
	int birthDay{};
	int birthYear{};
	int daysInMonth[13]{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	do
	{
		std::cout << "\n Year: ";
		std::cin >> birthYear;

		if (!std::cin)  // <--- Checks if you entered something other than a number and fixes "std::cin".
		{
			std::cout << "\n    Invalid Input! Must be a number\n";  // <--- Indented to show a problem. Sets it off in the output to the screen.

			std::cin.clear();  // <--- Resets the state bits.
			std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>. Clears the input buffer.

			continue;  // <--- causes a jump to the while condition to start over. Same for the others.
		}

		if (birthYear < 1900 || birthYear > 2020)
			std::cout << "\n    Invalid Year! Try again!\n";

	} while (birthYear < 1900 || birthYear > 2020);

	if ((birthYear % 4 == 0) && (birthYear % 100 != 0 || birthYear % 400 == 0))  // <--- Check for leap year.
		daysInMonth[2] = 29;

	do
	{
		std::cout << "\n Month: ";
		std::cin >> birthMonth;

		if (!std::cin)
		{
			std::cout << "\n    Invalid Input! Must be a number\n";

			std::cin.clear();
			std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

			continue;  // <--- causes a jump to the while condition to start over.
		}

		if (birthMonth < 1 || birthMonth > 12)
			std::cout << "\n    Invalid Month! Try again.\n";

	} while (birthMonth < 1 || birthMonth > 12);


	do
	{
		std::cout << "\n Day: ";
		std::cin >> birthDay;

		if (!std::cin)
		{
			std::cout << "\n    Invalid Input! Must be a number\n";

			std::cin.clear();
			std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.

			continue;  // <--- causes a jump to the while condition to start over.
		}

                //if (birthDay < 1 || birthDay > MAX-DAYS)
		if (birthDay < 1 || birthDay > daysInMonth[birthMonth])  // <--- Uses the array for correct number of days in a given month.
			std::cout << "\n    Invalid Day! Try again.\n";

	} while (birthDay < 1 || birthDay > daysInMonth[birthMonth]);

	std::cout << "\n Your birth date is: " << birthMonth << '/' << birthDay << '/' << birthYear << '\n';


	// <--- Keeps console window open when running in debug mode on Visual Studio. Or a good way to pause the program.
	// The next line may not be needed. If you have to press enter to see the prompt it is not needed.
	std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
	std::cout << "\n\n Press Enter to continue: ";
	std::cin.get();

	return 0;  // <--- Not required, but makes a good break point.
}

The comments in the program should explain what it is doing.

Line 17 I will get to later.

Lines 19 - 21 is the same as birthMonth = 0; it is known as the uniform initializer and is available from C++11 on. The empty {}s will set the variable to the type of zero based on the type of the variable. You also have the option of putting something inside the {}s to give it a value other than zero.

The do/while loops are all about the same. I did change it to ask for the year first which works with line 44 to determine if it is a leap year. Which is one of the reasons I used the array. The check for a failed "std::cin" may be ahead of what you need and can be removed if you want.

For line 83 the original code I wrote if (birthDay < 1 || birthDay > 31) the 31 is known as a magic number. This should be avoided. At the time it was easy to do and I realize I should not have done it. Right now your program is small and easy to change, but when it becomes 100 to 300 lines trying to find all these magic numbers will be difficult and you will likely miss 1 or 2 maybe more. With the constant variable you only have 1 place to make a change and it will change everywhere in the program. You could also create constant variables for min and max month, days and years.

Line 89 I added to display what you gathered as input. You can keep this or do something more at this line.

Lines 92 - 96 is just something I use. It is neither required or may not be needed. If you do not need it comment it out and keep for future reference.

As a whole keep this code for future reference. You may find it useful.

If there is any part that needs better explained let me know.

Andy
Topic archived. No new replies allowed.