I am developing a small program that asks for 4 integers one after the other using std::cin. I am using a function to request the integers, and passing in the maximum value allowed as an argument. To check if the the value is an integer i use std::cin.fail. The code which calls the functions is shown below.
cout << "Specify source number (1 - 1024)\n";
source = validate(1024);
cout << "Specify destination number (1 - 1024)\n"; // For all except data, the value is equal to the value returned by the validate function
destination = validate(1024); // The maximum possible value is passed in as an argument in each of the cases.
cout << "Specify type number (1 - 10)\n"; // User is prompted to enter the desired values.
type = validate(10);
cout << "Source port number (1 - 1024)\n";
port = validate(1024);
and the validate function code is shown below.
int validate(int max) {
int value; // Initialise variable to hold the value.
for (;;) { // Loop forever until correct input is entered.
cin >> value;
if (cin.fail()) { // If cin fails to receive a value matching the declared data type.
cout << "Characters are not permitted.\n"; // Notify the user of error.
cin.clear(); // Clear the error flag within cin.
cin.ignore(10000,'\n'); // Ignore the newline character in the buffer to prevent an infinite loop.
}
else if (value > max || value == 0) {
cout << "The value you have entered is not valid, please enter a number between 1 and " << max << "\n";
cin.clear(); // Clear the error flag within cin.
cin.ignore(10000, '\n'); // Ignore the newline character in the buffer to prevent an infinite loop.
}
else
break;
}
return value; // Return the validated value.
}
This works fine if the user enters just an integer, or just a letter, it validates as it should, the problem i am having is if a user enters for instance 34d or 22p. I get the error saying characters are not permitted but then it exits the function and moves onto the next request.
Tried rearranging and all sorts cant seem to solve it.
Any help greatly appreciated
What you want to do is to ignore the rest of the line even if you are successful:
int validate(int max) {
int value; // Initialise variable to hold the value.
for (;;) { // Loop forever until correct input is entered.
cin >> value;
if (cin.fail()) { // If cin fails to receive a value matching the declared data type.
cout << "Characters are not permitted.\n"; // Notify the user of error.
cin.clear(); // Clear the error flag within cin.
cin.ignore(10000,'\n'); // Ignore the newline character in the buffer to prevent an infinite loop.
}
else if (value > max || value == 0) {
cout << "The value you have entered is not valid, please enter a number between 1 and " << max << "\n";
cin.clear(); // Clear the error flag within cin.
cin.ignore(10000, '\n'); // Ignore the newline character in the buffer to prevent an infinite loop.
}
else {
cin.ignore(10000, '\n'); // Ignore the newline character in
break;
}
}
return value; // Return the validated value.
}
If you think this is too repetitive, you can do something like:
struct ignore_till_eol {
ignore_till_eol(std::istream& cin): input(cin) {}
~ignore_till_eol() { input.clear(); input.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); }
std::istream& input;
};
int validate(int max_value) {
for(;;) {
ignore_till_eol ignore(cin);
int v;
if( cin >> v && 0 < v && v <= max_value ) return v;
if( cin )
cout << "Value is invalid, must be between 1 and " << max_value << "\n";
else
cout << "Invalid characters in input\n";
}
}
or
int validate(int max_value) {
for(;;) {
ignore_till_eol ignore(cin);
int v;
if( !(cin >> v) )
cout << "Invalid characters in input\n";
else if( 0 < v && v <= max_value )
return v;
else
cout << "Value is invalid, must be between 1 and " << max_value << "\n";
}
}
Related
i new to programming and we are required to create a program that dont exit when the user inputs the wrong input, but i only learned the basics so far.. i already solved when the number is above and below 100 but when the user accidentally inserted a non integer it will go into a error loop. btw this is an average calculator.
#include <iostream>
using namespace std;
int main()
{
int num[100];
int n;
double sum, average;
cout << "how many numbers will you input?: ";
cin >> n;
while ( n > 100 || n <= 0 )
{
cout << "Error! number should in range of (1 to 100) only." << endl;
cout << "Enter the number again: ";
cin >> n;
}
for ( int i = 0; i < n; ++i )
{
cout << i + 1 << ". Enter number: ";
cin >> num[i];
sum += num[i];
}
average = sum / n;
cout << "Average = " << average;
}
If std::istream::operator >> fails, it will set failbit. Therefore, you should check failbit (for example by calling std::cin.fail()) to see whether the conversion was successful, before using the result of the conversion.
If the conversion fails due to bad input, then the next call to std::istream::operator >> will automatically fail due to failbit being set. That is why you are getting stuck in an infinite loop. If you want to attempt input again after a conversion failure, you will first have to clear failbit, by using the function std::cin.clear().
Also, you will have to discard the bad input that caused the conversion to fail, because otherwise, the next time you call std::istream::operator >>, the conversion will fail again for the same reason. In order to clear the bad input, you can use std::cin.ignore(), like this:
std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
In order to use std::numeric_limits, you will have to #include <limits>.
After performing these fixes on your code, it should look like this:
#include <iostream>
#include <limits>
using namespace std;
int main()
{
int num[100];
int n;
double sum, average;
bool input_ok;
//repeat until input is valid
do
{
cout << "How many numbers will you input? ";
cin >> n;
if ( cin.fail() )
{
cout << "Error: Conversion to integer failed!\n";
input_ok = false;
}
else if ( n > 100 || n <= 0 )
{
cout << "Error: Number should in range of (1 to 100) only!\n";
input_ok = false;
}
else
{
input_ok = true;
}
//clear failbit
cin.clear();
//discard remainder of line
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
} while ( !input_ok );
for ( int i = 0; i < n; ++i )
{
do
{
cout << i + 1 << ". Enter number: ";
cin >> num[i];
if ( cin.fail() )
{
cout << "Error: Conversion to integer failed!\n";
input_ok = false;
}
else
{
input_ok = true;
}
//clear failbit
cin.clear();
//discard remainder of line
cin.ignore( numeric_limits<streamsize>::max(), '\n' );
} while ( !input_ok );
sum += num[i];
}
average = sum / n;
cout << "Average = " << average;
}
This program has the following behavior:
How many numbers will you input? 200
Error: Number should in range of (1 to 100) only!
How many numbers will you input? -31
Error: Number should in range of (1 to 100) only!
How many numbers will you input? test
Error: Conversion to integer failed!
How many numbers will you input? 4abc
1. Enter number: 1
2. Enter number: 2
3. Enter number: 3
4. Enter number: 4
Average = 2.5
As you can see, the program now works in that it can now handle bad input such as test. It rejects that input and reprompts the user for new input.
However, one problem with this program is that it accepts 4abc as valid input for the number 4. It would probably be appropriate to reject such input instead. One way to fix this would be to inspect the remainder of the line, instead of simply discarding it.
Another issue is that this solution contains a lot of code duplication. Apart from the range check, both do...while loops are nearly identical. Therefore, it would be better to put this loop into a function, which can be called from several places in your code.
However, I generally don't recommend that you use std::istream::operator >>, because its behavior is not always intuitive. For example, as already pointed out above:
It does not always read a whole line of input, so that you must explicitly discard the remainder of the line.
It accepts 4abc as valid input for the number 4.
In my experience, if you want proper input validation of integer input, it is usually better to write your own function that reads a whole line of input using std::getline and converts it with std::stoi. If the input is invalid, then the function should automatically reprompt the user.
In my example below, I am calling this function get_int_from_user.
If you want to additionally ensure that the input is in a certain range, then you can call the function get_int_from_user in an infinite loop, and break out of that loop once you determine that the input is valid.
#include <iostream>
#include <string>
#include <sstream>
#include <cctype>
int get_int_from_user( const std::string& prompt );
int main()
{
int nums[100];
int n;
double sum;
//repeat loop forever, until input is good
for (;;) //equivalent to while(true)
{
n = get_int_from_user( "How many numbers will you input? " );
if ( 1 <= n && n <= 100 )
//input is good
break;
std::cout << "Error! Number should in range of (1 to 100) only.\n";
}
//read one number per loop iteration
for( int i = 0; i < n; i++ )
{
std::ostringstream prompt;
prompt << "Enter number #" << i + 1 << ": ";
nums[i] = get_int_from_user( prompt.str() );
sum += nums[i];
}
std::cout << "Average: " << sum / n << '\n';
}
int get_int_from_user( const std::string& prompt )
{
std::string line;
std::size_t pos;
int i;
//repeat forever, until an explicit return statement or an
//exception is thrown
for (;;) //equivalent to while(true)
{
//prompt user for input
std::cout << prompt;
//attempt to read one line of input from user
if ( !std::getline( std::cin, line ) )
{
throw std::runtime_error( "unexpected input error!\n" );
}
//attempt to convert string to integer
try
{
i = std::stoi( line, &pos );
}
catch ( std::invalid_argument& )
{
std::cout << "Unable to convert input to number, try again!\n";
continue;
}
catch ( std::out_of_range& )
{
std::cout << "Out of range error, try again!\n";
continue;
}
//The remainder of the line is only allowed to contain
//whitespace characters. The presence of any other
//characters should cause the entire input to get rejected.
//That way, input such as "6sdfj23jlj" will get rejected,
//but input with trailing whitespace will still be accepted
//(just like input with leading whitespace is accepted by
//the function std::stoi).
for ( ; pos < line.length(); pos++ )
{
if ( !std::isspace( static_cast<unsigned char>(line[pos]) ) )
{
std::cout << "Invalid character found, try again!\n";
//we cannot use "continue" here, because that would
//continue to the next iteration of the innermost
//loop, but we want to continue to the next iteration
//of the outer loop
goto continue_outer_loop;
}
}
//input is valid
return i;
continue_outer_loop:
continue;
}
}
This program has the following behavior:
How many numbers will you input? 200
Error! Number should in range of (1 to 100) only.
How many numbers will you input? -31
Error! Number should in range of (1 to 100) only.
How many numbers will you input? test
Unable to convert input to number, try again!
How many numbers will you input? 4abc
Invalid character found, try again!
How many numbers will you input? 4
Enter number #1: 1
Enter number #2: 2
Enter number #3: 3
Enter number #4: 4
Average: 2.5
As you can see, it now correctly rejects the input 4abc.
I believe that using the function get_int_from_user makes the code in main much cleaner.
Note that the code above uses one goto statement. Under most circumstances, you should avoid using goto, but for breaking out of nested loops, it is considered appropriate.
#include <iostream>
#include <limits>
using namespace std;
int avg(int sum, int n)
{
int average;
average = sum/n;
cout<<"Avg = "<<average;
}
int main()
{
int n;
cout << "how many numbers will you input?: ";
cin >> n;
int w = n;
int num[w];
int sum;
while(n>100||n<=0)
{
cout << "Error! number should in range of (1 to 100) only." << endl;
cout << "Enter the number again: ";
cin >> n;
}
for(int i = 0; i < n; ++i)
{
cout << i + 1 << "Enter number: ";
cin >> num[i];
if(!cin)
{
cout << "Wrong Choice. " << endl;
cin.clear();
cin.ignore( numeric_limits<std::streamsize>::max(), '\n' );
n++;
continue;
}
else
{
sum += num[i];
}
}
cout<<"Sum = "<<sum<<endl;
avg(sum,w);
}
This code works fine if I enter something that isn't a number in, e.g. F: it will print the error message. However, if I enter e.g. 2F2 or , it will take the 2 and pass the check, continue in my code and on the next cin >> statement it will put the F in, and then it loops back and puts the 2 in.
How do I make it so it only accepts a single number e.g. 2 and not e.g. 2F2 or 2.2?
int bet = 0;
// User input for bet
cout << " Place your bet: ";
cin >> bet;
cout <<
// Check if the bet is a number
if (!cin.good())
{
cin.clear();
cin.ignore();
cout << endl << "Please enter a valid number" << endl;
return;
}
bool Checknum(std::string line) {
bool isnum = true;
int decimalpoint = 0;
for (unsigned int i = 0; i < line.length(); ++i) {
if (isdigit(line[i]) == false) {
if (line[i] == '.') {
++decimalpoint; // Checks if the input has a decimal point that is causing the error.
}
else {
isnum = false;
break;
}
}
}
if (decimalpoint > 1) // If it has more than one decimal point.
isnum = false;
return isnum;
}
If you take a string from the user, this should work. You can convert the string to an integer or a float(stoi or stof, respectively). It may not be the best solution there is, but this is what I have. Excuse the indentation.
Do getline to read one whole line of input from cin.
Create a stringstream to parse the string you got.
In this parser, read the number; if it fails - error
Read whitespace; if it doesn't arrive to the end of string - error
#include <sstream>
...
int bet = 0;
std::cout << " Place your bet: ";
while (true)
{
std::string temp_str;
std::getline(cin, temp_str);
std::stringstream parser(temp_str);
if (parser >> bet && (parser >> std::ws).eof())
break; // success
cout << endl << "Please enter a valid number" << endl;
}
This code keeps printing the error message until it receives valid input. Not sure this is exactly what you want, but it's pretty customary UI.
Here >> ws means "read all the whitespace". And eof ("end of file") means "end of the input string".
I just started learning C++ after previously coding with Java. The code below takes input from the user and validates the input. The first piece asks for the number of voters, which must be a positive number. If I enter a negative number the program behaves as I expected. It prints out the error message and asks for the input again. However, if I enter any other character, such as any alphabet letter I get an infinite loop in the console, asking for input and printing the error message. What am I doing wrong?
my code:
#include <iostream>
using namespace std;
struct dataT {
int numOfVoters = -1;
float preSpread = -1;
float votingError = -1;
};
void getUserInfo() {
dataT data;
while (data.numOfVoters == -1) {
cout << "enter the number of voters" << endl;
cin >> data.numOfVoters;
if (data.numOfVoters <= 0) {
data.numOfVoters = -1;
cout << "Invalid entry, please enter a number larger than zero." << endl;
}
}
while (data.votingError == -1) {
cout << "enter the percentage spread between candidates" << endl;
cin >> data.votingError;
if (data.votingError <= 0 || data.votingError >= 1) {
data.votingError = -1;
cout << "Invalid entry. Enter a number between 0 to 1." << endl;
}
}
while (data.preSpread == -1) {
cout << "Enter the precentage spread between the two candidates." << endl;
cin >> data.preSpread;
if (data.preSpread <= 0 || data.preSpread >= 1) {
data.preSpread = -1;
cout << "Invalid input. Enter a number between 0 and 1." << endl;
}
}
}
int main() {
getUserInfo();
return 0;
}
Console:
enter the number of voters
f
Invalid entry, please enter a number larger than zero.
enter the number of voters
Invalid entry, please enter a number larger than zero.
enter the number of voters
Invalid entry, please enter a number larger than zero.
...
...
...
If you write cin >> integervariable but in cin there are character that cannot represent an integer, the input fails, !cin becomes true, and the character remain there until you don't reset the input state from the error and consume the wrong characters.
a proper check can be
while(integervariable has not good value)
{
cout << "prompt the user: ";
cin >> integervariable;
if(!cin) //something wrong in the input format
{
cin.clear(); //clear the error flag
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); //discard the rubbish
cout << "prompt error message \n";
}
}
Your if statements are always true, you want something more like:
if (data.votingError < 0 || data.votingError > 1) {
...
then data.votingError can take on a value different from -1 and exit your loop.
The std::cin object will check whether or not it is in a valid state every time it reads. If you enter a char where your program expects an int, then you'll "break" the input stream. All subsequent calls to std::cin will then be effectively skipped until you manually reset the input stream. When this happens, you'll never be able to set your values to anything other than -1, and your if statement always evaluates to true, causing an infinite loop.
As an aside, you can check for failure state by including && cin in all of your tests. Input objects implicitly evaluate to true if the stream is in a valid state and to false if the stream is in a failure state instead.
I'm trying to filter out invalid user inputs in a small C++ program using the following chunk of code:
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
cin >> selection;
if (!(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
}
}
It seems to work fine when I enter any numerical value that is either inside or outside the range i want to filter. However strange things happen when I enter invalid values such as values larger than the maximum storable int or characters. The code loops through and skipping the "cin" command.
How do I fix this?
Thanks
You need to detect unconvertible input using fail() and then ignore the rest of the bad data and reset cin error flags using clear() before reading in a new input attempt.
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
cin >> selection;
if (cin.fail() || !(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
cin.clear();
cin.ignore(std::numeric_limits<int>::max(), '\n');
}
}
Here are two suggestions for fixing your issue:
Add Error Handling to cin
Read as String And Parse
The common solution is Read As String And Parse, but I'm presenting both for you to choose.
Add Error Handling to cin
When the stream extraction function receives a character that is not suited for numerics, it sets a fail bit. You need to check the state of the stream (cin) for failure. If you want to continue, you need to clear the error state.
The state can be checked by using the fail method: cin.fail(). To clear the state use: cin.clear().
See C++ Online Reference -- istream
Read As String And Parse
An alternative is read the input as a string, then parse the string for your data. The string container has some useful methods for parsing.
Use getline to read in a string variable from cin.
Again, you will have to write code to check for errors and process them.
You need to check condition of cin, and if it is any error states, you should clear the error flag and ignore():
int main()
{
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
if (cin >> selection)
{
if (!(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
}
}
else
{
cin.clear();
cin.ignore();
}
}
cout << selection << " selected\n";
return 0;
}
Another way is to read everything as string and let the stringstream do error check:
int main()
{
int selection = -1;
while (!(selection >= 1 && selection <=4))
{
string line;
getline(cin, line);
stringstream sstr(line);
if (sstr >> selection)
{
if (!(selection >= 1 && selection <=4))
{
cout << "invalid selection!" << endl;
cout << "selection: ";
}
}
else
{
cout << "invalid input!" << endl;
cout << "selection: ";
}
}
cout << selection << " selected\n";
return 0;
}
Note: You may need to put a cin.ignore(); right after getline() call - depending on if you are reading all unformatted input or not.
You should read cin into a std::string and start your validation from there. Check that the value is numeric, for starters.
When you enter letters for example it puts cin into a bad state and leaves those characters. You need to ignore them (cin.ignore(1024, '\n')) so it knows to move on to the next line of input.
I am trying to get input from the user and need to know a way to have the program recognize that the input was or was not a double/char this is what i have right now... but when you type an incorrect type of input
1) the double test one just loops infinatly
2) the char one won't stop looping even with the correct imput
int main () {
double _double = 0;
bool done = true;
while ( done ) {
cout << "Please enter a DOUBLE:\n" << endl;
cin >> _double;
if ( _double > 0 ) { done = false; }
if ( _double < 0 ) { cout << "\nthe number you entered was less than zero\nplease enter a valad number..." << endl; }
if(cin.fail()) { cin.clear(); }
}
done = false;
char _char = ' ';
while ( !done ) {
cout << "Please enter a CHAR" << "\n";
cout << "\t'y' = yes\n\t'n' = no" << endl;
cin >> _char;
if ( _char == 'y' || _char == 'n' ) { done = true; }
if ( ! (_char == 'y' || _char == 'n') ) { cout << "\nyou have entered an invald symbol... \n" << endl; }
if(cin.fail()) { cin.clear(); }
}
The best bet is always to read your input as strings. You can then use functions like std::strtod() to test and convert to doubles. Checking if streams have failed and then resetting them is error prone at best, and doesn't give you the possibility of producing good error messages.
For example:
string s;
cin >> s;
char * p;
double d = strtod( s.c_str(), & p );
if ( * p == 0 ) {
cout << "Read double: " << d << endl;
}
else {
cout << "Read string: " << s << endl;
}
The pointer 'p' will point to the first character that cannot be converted to a double. How exactly you handle that really depends on your app's logic.
The problem is that when you read something and cin sees the input can never be a double, it stops reading, leaving the stuff in the buffer that it didn't consume. It will signal failure, which you clear but you won't eat the remaining input that cin didn't eat up. So, the next time the same wrong input is tried to read again, and again...
The problem with the char one is that you have to press the return key to make it process any characters on most terminals (this does not happen if you make your program read from a file, for instance). So if you press y then it won't go out of the read call, until you hit the return key. However, then it will normally proceed and exit the loop.
As others mentioned you are better off with reading a whole line, and then decide what to do. You can also check the number with C++ streams instead of C functions:
bool checkForDouble(std::string const& s) {
std::istringstream ss(s);
double d;
return (ss >> d) && (ss >> std::ws).eof();
}
This reads any initial double number and then any remaining whitespace. If it then hit eof (end of the file/stream), it means the string contained only a double.
std::string line;
while(!getline(std::cin, line) || !checkForDouble(line))
std::cout << "Please enter a double instead" << std::endl;
For the char, you can just test for length 1
std::string line;
while(!getline(std::cin, line) || line.size() != 1)
std::cout << "Please enter a double instead" << std::endl;
If you want to read only 1 char and continue as soon as that char was typed, then you will have to use platform dependent functions (C++ won't provide them as standard functions). Look out for the conio.h file for windows for instance, which has the _getch function for this. On unix systems, ncurses provides such functionality.
cin >> _double will always get you a double, whether they typed in "42", "0" or "mary had a little lamb". You need to read the user input as a string, then test that string to see if it is a double. sscanf will return 0 if it can't convert the input string to the desired type:
cout << "Please enter a DOUBLE:\n" << endl;
string s;
cin >> s;
if( !sscanf(s.c_str(), "%lf", &_double) )
{
done = false;
cout << "Not a number, sparky. Try again." << endl;
continue;
}
Also, identifiers with leading underscores like you have are reserved by the language. Don't get in the habit of naming things like _double -- someday, they may not work.