input of letter or symbol crashes program

Hi everyone,

I have been working through the www.cplusplus.com tutorials and I decided to create a program to work out circle areas and circumferences. It is slightly advanced, with the option for the user to select what "function" to use (area, circumference or both) and then the option to go back to the "menu" and select another "function".

FYI This is the "menu":
1
2
3
    cout << "Please select which function is required. For area calculations, type 1. For circumference calculations, type 2.";
    cout << "For both circumference and area calculations, type 3. To exit program, type 0. Once the number is entered, press return: ";
    cin >> which_function;


The program works fine and I am very pleased :) However, I am running into a few difficulties making it "idiot proof"!

There are two problems:

1. If the user inputs any symbol or letter other than an integer then the program behaves strangely. If it is entered in the menu, then it is treated as a zero and it follows:
if (which_function == 0)
Instead I want it to:
1
2
3
4
if (CONDITION)
{
cout << "Invalid character. Please enter a number.";
}

However, I don't know what to put as the condition.
If the user inputs any symbol or letter other than an integer in a "function" such as the area "function":
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
//AREA FUNCTION
if (which_function == 1)
    {

    do
    {
    cout << "If circle area is desired, please enter a radius length. Otherwise, to exit area function, enter 0. ";
    cout << "Once entered, press the return key: ";
    cin >> r; cout << endl;

    if (r > 0)
    {
    r_squared = r * r;
    area = r_squared * pi;

    cout << "Your radius is: "; cout << r; cout << endl;
    cout << "The area of your circle is: "; cout << area; cout << endl; cout << endl;
    }

    else if (r < 0)
    {
    cout << "A radius cannot be a negative number. Please try again with a postive number."; cout << endl; cout << endl;
    }
    }
    while (r != 0);

    }

Then it just goes completely crazy. If you run the program you will see for yourself, but otherwise I *think* it gets stuck in an infinite loop, because it just keeps repeating the menu message forever. How do I stop this?

2. The second problem I think is related to the declaration part:
1
2
3
4
5
6
    double which_function;
    double r;
    double r_squared;
    double pi_r;
    double area;
    double circumference;

I think because of the use of 'double' the computer can't cope with a number that has to many numbers. Here's why; If I input 123456789 as a radius then it tells me my radius is 1.234567e+008 Assuming that means 1.234567 * 10^8 the number is incorrect. A similar thing happens if the radius produces an area/circumference that is too large or if the radius is something like 0.123456789
How do I make these numbers possible or what IF statement can I use to stop people if they input these numbers?

Here is the full code:
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <iostream>

using namespace std;

#define pi 3.14159

int main()
{
    double which_function;
    double r;
    double r_squared;
    double pi_r;
    double area;
    double circumference;


    cout << "Hello and welcome! This program will allow you to calculate the area and/or circumference of any circle."; cout << endl; cout << endl;


    do
    {



    cout << "Please select which function is required. For area calculations, type 1. For circumference calculations, type 2.";
    cout << "For both circumference and area calculations, type 3. To exit program, type 0. Once the number is entered, press return: ";
    cin >> which_function;
    cout << endl;

    if (which_function == 1)
    {

    do
    {
    cout << "If circle area is desired, please enter a radius length. Otherwise, to exit area function, enter 0. ";
    cout << "Once entered, press the return key: ";
    cin >> r; cout << endl;

    if (r > 0)
    {
    r_squared = r * r;
    area = r_squared * pi;

    cout << "Your radius is: "; cout << r; cout << endl;
    cout << "The area of your circle is: "; cout << area; cout << endl; cout << endl;
    }

    else if (r < 0)
    {
    cout << "A radius cannot be a negative number. Please try again with a postive number."; cout << endl; cout << endl;
    }
    }
    while (r != 0);

    }

    else if (which_function == 2)
    {

    do
    {
    cout << "If circumference is desired, please enter a radius length. Otherwise, to exit circumference function, enter 0. ";
    cout << "Once entered, press the return key: ";

    cin >> r; cout << endl;

    if (r > 0)
    {
    pi_r = pi * r;
    circumference = 2 * pi_r;

    cout << "Your radius is: "; cout << r; cout << endl;
    cout << "The circumference of your circle is: "; cout << circumference; cout << endl; cout << endl;
    }

    else if (r < 0)
    {
    cout << "A radius cannot be a negative number. Please try again with a postive number."; cout << endl; cout << endl;
    }
    }
    while (r != 0);

    }
    else if (which_function == 3)
    {
     do
    {
    cout << "If circumference and radius are desired, please enter a radius length. Otherwise, to exit circumference and radius function, enter 0. ";
    cout << "Once entered, press the return key: ";

    cin >> r; cout << endl;

    if (r > 0)
    {
    pi_r = pi * r;
    circumference = 2 * pi_r;
    r_squared = r * r;
    area = r_squared * pi;

    cout << "Your radius is: "; cout << r; cout << endl;
    cout << "The area of your circle is: "; cout << area; cout << endl;
    cout << "The circumference of your circle is: "; cout << circumference; cout << endl; cout << endl;
    }

    else if (r < 0)
    {
    cout << "A radius cannot be a negative number. Please try again with a postive number."; cout << endl; cout << endl;
    }
    }
    while (r != 0);

    }
    else if (which_function == 0)
    {
    cout << "Thank you for using this program! ";
    }
    else
    {
    cout << "You have entered an invalid number. Please try again using no spaces and only numbers 1, 2, 3 or 0."; cout << endl; cout << endl;
    }



    }
    while (which_function != 0);


    cout << "Enter any key to exit."; cout << endl;
    return 0;

}


Thank you very much for reading and helping me!
I'm pretty sure you can satisfy your if condition with the #include <cctype> and this:

1
2
3
4
5
if (isdigit(varName) == false)
{
cout << "Invalid character.\n"; 
cin >> varName; 
}


That will only check one iteration of the code. if you create a validate function for it, though, it will keep checking it until it's right.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
validate(varName)
while (validate == false) 
{
cout << "Invalid character.\n"; 
cin >> varName; 
validate(varName);
}

bool validate(int a)
{
if (isdigit(a) == false) 
return false; 
else
return true; 
}



This is just a mock up and simple but it should suffice. Also, I THINK !isdigit(varName) has the same value as isdigit(varName) == false. Not 100% though. Also, you may want to do some digging and see if isdigit() strips the ascii code and uses that if it is a non-integral type. Idt it does but I've been wrong in the past and am FAR from a pro.

Also, a switch might be better for your menu instead of a list of ifs.

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
 

main()
{
int c; 
cout << "Menu\n" 
      << "\t1 -- Do Something\n"
      << "\t2 -- Do Something else\n" 
      << "\t3 -- Do Final Something\n"; 
cin >> c; 

switch (c)
{
case 1: 
doSomething();
break; 
case 2: 
doSomethingElse(); 
break; 
case 3: 
doFinalSomething(); 
break; 
default: 
cout << "\n\nOops. PEBKAC!!! Returning to menu.\n\n"; 
menu(); 
}

}


Note: No menu() function exists. For the purpose of ease in hw exercises that required a menu, I just made a menu function, threw it in main, and passed whatever vars I needed to.
Last edited on
That only works if you're reading a string in first and then parse that later. You can't verify if an int is an int cause an int is always an int, obviously.

After reading something with an istream, you can check if the fail-flag is set with istream::fail (call cin.fail() in this case). This flag is set if the input could not be interpreted as what you were trying to read in. Then just keep telling your user not to be an idiot til he gets it right.

Read more about this here:
http://www.cplusplus.com/reference/iostream/istream/
^^ Absolutely correct. I just implemented it for my final project.

1
2
3
4
5
6
7
8
while (cin.fail())  //while the data type declared doesn't match data type entered
	{
	cout << "Invalid Choice.  Select again: ";  //error msg
				cin.clear();   //clear istream
				cin.ignore(std::numeric_limits<int>::max(),'\n');  //repair istream
				cin >> choice; //get user input again
	}
Hey,

Thanks for the great responses. I have now cleaned up the whole structure by using a switch contained within a loop.

Also, I have fixed the issue of non integer values by creating a boolean than equalled cin.fail() and using the result to determine whether the input was an integer or not. Here is the new area "function":
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
case 1:
        do
        {
        cout << "If circle area is desired, please enter a radius length. To return to menu, enter 0. ";
        cout << "Once entered, press the return key: ";
        cin >> r; cout << endl;

        r_fail = cin.fail();

        while (r_fail == true)
        {
        cin.clear();
        cin.ignore();

        cout << "Invalid input. Please only input numerical values."; cout << endl; cout << endl;
        cout << "If circle area is desired, please enter a radius length. Otherwise, to exit area function, enter 0. ";
        cout << "Once entered, press the return key: ";
        cin >> r; cout << endl;

        r_fail = cin.fail();
        }

        if (r > 0)
        {
        r_squared = r * r;
        area = r_squared * pi;

        cout << "Your radius is: "; cout << r; cout << endl;
        cout << "The area of your circle is: "; cout << area; cout << endl; cout << endl;
        }

        else if (r < 0)
        {
        cout << "A radius cannot be a negative number. Please try again with a postive number."; cout << endl; cout << endl;
        }
        }
        while (r != 0);
    break;


However. This does cause another problem to arise. If the user inputs, for example, "a" then the message in the while (r_fail == true) loop will be displayed once. However, if the user inputs more than one letter, for example "aa" then the message will be displayed twice. For "aaa" three times, etc. How do I get the message to be displayed only once?

Also, re my other initial problem, how can I get the program to deal with an (almost) infinitely long number as the radius?

Thanks again for the great help!
All user input ends when the user presses ENTER.

http://www.cplusplus.com/forum/general/52964/#msg286993

http://www.cplusplus.com/forum/beginner/18258/ (read the whole thread)

Hope this helps.
[Help still needed]

Hi,

Thanks for the links. I looked through them all and at some of your other posts but I'm still really struggling to figure out what to do.

All I want is some code that will check that the user has entered a number and if not them give them a telling off. Is there anything that can do this?
There are at least four methods of doing that in the links I provided.
Sorry, I'm really trying not to sound like an annoying n00b, but I can't understand this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
bool TEST::validateFloat(string input) {
    bool valid = true;
    float vf;

    if (input.length() == 0) {
        valid = false;
    } else {
//cannot have alphabet or characters
        for (int i = 0; i < input.length(); i++) {
            if (!isdigit(input[i])) {
                valid = false;
            }
        }
    }
    vf = atof(input.c_str());
//accept negative values
    if (vf < 0) {
        valid = true;
    }
    return valid;
}



In particular the bit following //cannot have alphabet or characters. Would you mind explaining it?

Thanks.
Wait, I post a link and you take an example from the OP who is having trouble with his code?
Why not choose code that works, like some of the suggestions I made?
Oh whoops, sorry I didn't realise (not really helping the annoying n00b image here...).

This is something you posted, but I'm struggling to understand it:
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
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

//----------------------------------------------------------------------------
template <typename T>
struct input_t
  {
  mutable T& n;
  explicit input_t( T& n ): n( n ) { }
  input_t( const input_t <T> & i ): n( i.n ) { }
  };

//----------------------------------------------------------------------------
template <typename T>
inline
input_t <T>
input( T& n )
  {
  input_t <T> result( n );
  return result;
  }

//----------------------------------------------------------------------------
template <typename T>
istream& operator >> ( istream& ins, const input_t <T> & i )
  {
  // Read a line (terminated by ENTER|NEWLINE) from the user
  string s;
  getline( ins, s );

  // Get rid of any trailing whitespace
  s.erase( s.find_last_not_of( " \f\n\r\t\v" ) + 1 );

  // Read it into the target type
  istringstream ss( s );
  ss >> i.n;

  // Check to see that there is nothing left over
  if (!ss.eof())
    ins.setstate( ios::failbit );

  return ins;
  }

//----------------------------------------------------------------------------
int main()
  {
  int n;

  cout << "Please enter an integer> " << flush;
  cin >> input( n );
  while (!cin)
    {
    cin.clear();
    cout << "Please, enter only an INTEGER> " << flush;
    cin >> input( n );
    }

  cout << "Good job!\n"
          "You entered the number " << n << endl;

  return 0;
  }
The important stuff is on lines 29 through 41. You can put it into a little procedure if you like.

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
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

bool getIntFromUser( int& result )
  {
  // Read a line (terminated by ENTER|NEWLINE) from the user
  string s;
  getline( cin, s );

  // Get rid of any trailing whitespace
  s.erase( s.find_last_not_of( " \f\n\r\t\v" ) + 1 );

  // Convert the user's input into an integer, if possible
  istringstream ss( s );
  ss >> result;

  // Return whether there is nothing left over.
  // (If there is, then the user did not enter a number.)
  return ss.eof();
  }

int main()
  {
  int which_function;
  while (true)
    {
    // Notice that you don't have to tell the user to press ENTER to make his selection.
    // (He already knows to do that.)
    cout << "Please select which function is required:\n"
            " 1 - Area calculations\n"
            " 2 - Circumference calculations\n"
            " 3 - Both circumference and area calculations\n"
            " 0 - Exit\n"
            "> ";

    // If we successfully get an integer, in range, from the user, we're done with this input.
    if (getIntFromUser( which_function )
    && (which_function >= 0)
    && (which_function <= 3))
      break;

    // Otherwise, we need to let the user know he messed up and try again.
    cout << "Invalid choice. Try again.\n";
    }

Getting input is actually one of the more difficult things to do. CS courses tend to skip over all but the very most basic parts of it, so when people are ready to start bullet-proofing their user inputs, they often have the same difficulties you are having.

Hope this helps.
Thank you so much for the code! I have been using Google a lot and I now *think* that I understand everything used in the code.

However, the boolean is not working for me. In attempt to fix the problem I have isolated the boolean into a simple program. Here is the code:
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
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main ()
{
    string which_function_string;
    int which_function_int;

    cout << "enter an integer: ";
    cout << endl;

    cin >> which_function_string;

    bool is_input_int (int & which_function_int)
    {
        getline (cin, which_function_string);

        which_function_string.erase(which_function_string.find_last_not_of ("\f\n\r\t\v") +1 );

        stringstream ss (which_function_string);

        ss >> which_function_int;

        return ss.eof();
    }

    if (is_input_int == 0)
    {
        cout << "You did not enter a valid number.";
    }

    return 0;

}


When I tried to compile the code I got 2 warnings:

Line 18: error: a function-definition is not allowed here before '{' token
Line 30: error: 'is_input_int' was not declared in this scope

I also got 1 warning:

Line 10: warning: unused variable 'which_function_int'


Thanks for the help so far, and once again - please help!
You cannot nest functions. You need to spend some time going through the tutorials on this site.
Good luck!
Topic archived. No new replies allowed.