Not handling user input correctly - c++

So, this program I am working on is not handling incorrect user input the way I want it to. The user should only be able to enter a 3-digit number for use later in a HotelRoom object constructor. Unfortunately, my instructor doesn't allow the use of string objects in his class (otherwise, I wouldn't have any problems, I think). Also, I am passing the roomNumBuffer to the constructor to create a const char pointer. I am currently using the iostream, iomanip, string.h, and limits preprocessor directives. The problem occurs after trying to enter too many chars for the roomNumBuffer. The following screenshot shows what happens:
The relevant code for this problem follows:
cout << endl << "Please enter the 3-digit room number: ";
do { //loop to check user input
badInput = false;
cin.width(4);
cin >> roomNumBuffer;
for(int x = 0; x < 3; x++) {
if(!isdigit(roomNumBuffer[x])) { //check all chars entered are digits
badInput = true;
}
}
if(badInput) {
cout << endl << "You did not enter a valid room number. Please try again: ";
}
cin.get(); //Trying to dum- any extra chars the user might enter
} while(badInput);
for(;;) { //Infinite loop broken when correct input obtained
cin.get(); //Same as above
cout << "Please enter the room capacity: ";
if(cin >> roomCap) {
break;
} else {
cout << "Please enter a valid integer" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
for(;;) { //Infinite loop broken when correct input obtained
cout << "Please enter the nightly room rate: ";
if(cin >> roomRt) {
break;
} else {
cout << "Please enter a valid rate" << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
Any ideas would be greatly appreciated. Thanks in advance.

Read an integer and test whether it's in the desired range:
int n;
if (!(std::cin >> n && n >= 100 && n < 1000))
{
/* input error! */
}

Although Kerrek SB provide an approach how to address the problem, just to explain what when wrong with your approach: the integer array could successfully be read. The stream was in good state but you didn't reach a space. That is, to use your approach, you'd need to also test that the character following the last digit, i.e., the next character in the stream, is a whitespace of some sort:
if (std::isspace(std::cin.peek())) {
// deal with funny input
}
It seems the error recovery for the first value isn't quite right, though. You probably also want to ignore() all characters until the end of the line.

Related

How to reject char inputs in cin and define minimum and maximum int values?

I have a program which has the ability to reject user input if a char is entered instead of an int, and this works almost perfectly - anything entered that isn't a number is being rejected.
However, all of these cins need to accept any value between a minimum and a maximum, but I can't get it to work. The code below shows my efforts so far, but there's a slight bug. If a char is entered, followed by an int that is out of range, and another char is entered (I like to test rigorously - I mean, who knows what could happen if an actual end user came across the problem) the program throws the final value of mortgageTerm out as 0.
Could anyone tell me where I'm going wrong and give me any pointers to help me fix it? Thanks in advance to anyone who's able to help me solve my problem!
int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm))
{
cout << "That's not a valid choice! Try again : ";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
while (getline(cin, line))
{
stringstream linestream;
if (!linestream >> mortgageTerm)
{
cout << "Input was not a number! Try again : ";
cin >> mortgageTerm;
continue;
}
if ((mortgageTerm <= 0 || mortgageTerm > 40))
{
cout << "Input out of range. Try again : ";
cin >> mortgageTerm;
continue;
}
char errorTest;
if (linestream >> errorTest)
{
cout << "Invalid input. Try again : ";
cin >> mortgageTerm;
continue;
}
break;
}
cout << mortgageTerm;
You're almost there. Your first issue is your first while loop is not needed at all. Then we just need to tweak the second loop to make sure that all the input read was used in the value you get. We can also simplify it by using a single error statement, Making those changes gives you
int mortgageTerm;
string line;
cout << "Mortgage term (1 - 40 years) : ";
while (getline(cin, line)) // consume all input given
{
stringstream linestream(line); // you have to construct the stream from the string here
linestream >> mortgageTerm; // try and read the data
if (!linestream.eof() || mortgageTerm <= 0 || mortgageTerm > 40)
{
// either there is input left in linestream or the value is not in range
cout << "Invalid input. Try again : ";
}
}
Just check for the minimum and maximum in the same condition where you check if it was able to be converted into an int, using ||, in a condition the expressions are checked left to right in order, so the first did its work already when you evaluate the second and mortageTerm will have the value.
Edited to address comments.
int mortgageTerm;
cout << "Mortgage term (1 - 40 years) : ";
while (!(cin >> mortgageTerm) ||
mortageTerm < 1 ||
mortgageTerm > 40 )
{
cout << "That's not a valid choice! Try again : ";
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
}
// If you are concerned about extra input after the number and want to clear the input stream
// cin.ignore(std::numeric_limits<streamsize>::max(), '\n');

C++ mystical infinite loop

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.

Use cin.clear() or cin,ignore() for ONLY previous line input?

Is there a way to use cin.clear() or cin,ignore() for ONLY the previous line of input without clearing ALL previous input? For example in my code I prompt the user for input for each month, well if the input is less than 0 I would like the program to clear that negative input so that it does not get total into my calculation. Problem is it clears ALL previous input which still screws up the calculation. Thanks for any ideas in advance.
// prompt user for input, keep a total sum of data entered
for(int i = 0; i < 12; i++)
{
cout << "Enter total rainfall for " << year[i] << endl;
cin >> month[i];
total += month[i];
while(month[i] < 0)
{
cout << "Ony enter positive numeric values!" << endl;
cin.clear();
cin >> month[i];
}
}
You seem to be mixing up two distinct issues. ios_base::clear()
doesn't remove any input; it resets the error status of the stream.
istream::ignore( n, ch ), on the other hand, reads forward until n
characters have been extracted or a character ch is seen'
myInput.ignore( INT_MAX, '\n' ) should ignore everything up to (and
including) the next '\n'.
but your code has one thing that is very strange:
cin << month[i];
You can't output to std::cin.
You probably want something like this:
unsigned int month[12];
for(size_t i = 0; i < 12; i++)
{
cout << "Enter total rainfall for month " << i+1 << endl;
while (!(cin >> month[i]))
{
cout << "Invalid input, try again." << endl;
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
// you probably want to repeat ignore() stuff here
// so in case user inputs something like 10x, the "x" that was left
// in the stream gets discarded
}
Note the array of unsigned integers. This way input like "-5" is automatically considered invalid so you don't need the less than 0 check. Clearing the entire stream shuouldn't worry you - if the input operation succeeds, the relevant part is already stored in your array.

How do I make a C++ program that filter out non-integers?

Something like this
cout << "Enter the number of columns: " ;
cin >> input ;
while( input != int ){
cout << endl <<"Column size must be an integer"<< endl << endl;
cout << "Enter the number of columns: " ;
cin >> input ;
}
cin will do this for you, kind of. cin will fail if it receives something that is not of the same type as input. What you can do is this:
int input;
while(!(cin >> input))
{
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << endl <<"Column size must be an integer"<< endl << endl;
cout << "Enter the number of columns: " ;
}
The cin.clear() clears the error bits, and cin.ignore() clears the input stream. I'm using number_limits to get the maximum size of the stream, that requires that you #include<limits>. Alternatively you can just use a big number or a loop.
You can't do it like that; input has to have some concrete type.
The simplest approach that will work is to read a string from cin, then convert it to an integer in a second step with strtol or one of its relatives, and issue an error message if strtol doesn't consume the whole string.
#include<iostream.h>
using namespace std;
int main()
{
int x;
int input;
while(!0){
cout<<"Enter your option :";
cout<<"1 .Enter Column size :"<<endl;
cout<<"2.Exit "<<endl;
cin>>x;
switch(x)
{
case 1: cout << "Enter the number of columns: "<<endl ;
cin>>input;
if(input>0)
cout << "The number of columns: is "<<input<<endl ;
else
cout << "Enter the number of columns as integer "<<endl ;
case 2:exit(0);
}
};
return 0;
}
Many of the answers here use the cin's built in filter. While these work to prevent a char or string from being entered, they do not prevent a float entry. When a float is entered, it is accepted and the decimal value remains in the buffer. This creates problems with later requests to cin. The following code will check the cin error flag and also prevent float inputs.
*note: The cin.ignore statement may require some tweaking to fully bullet proof the code.
void main()
{
int myint;
cout<<"Enter an integer: ";
intInput(myint);
}
void intInput(int &x)
{
bool valid = true; //flag used to exit loop
do
{
cin>>x;
//This 'if' looks for either of the following conditions:
//cin.fail() returned 'true' because a char was entered.
//or
//cin.get()!='\n' indicating a float was entered.
if(cin.fail() || cin.get()!='\n')
{
cout<<"Error. The value you entered was not an integer."<<endl;
cout<<"Please enter an integer: ";
cin.clear(); //clears cin.fail flag
cin.ignore(256,'\n'); //clears cin buffer
valid = false; //sets flag to repeat loop
}
else valid = true; //sets flag to exit loop
}while(valid == false);
}
This is a very basic solution to your problem that newer programers should find useful for people trying to break their programs. Eventually there are more advanced and efficient ways to do this.
int input;
int count = 1;
while(count == 1){ //this is just a simple looping design
cin >> input;
if(cin.fail()){ //If the input is about to crash your precious program
cin.clear(); //Removes the error message from internal 'fail safe'
cin.ignore(std::numeric_limits<int>::max(), '\n'); //Removes the bad values creating the error in the first place
count = 1; //If there is an error then it refreshes the input function
}
else{
count--; //If there is no error, then your program can continue as normal
}
}
Here is the advanced code: stackoverflow.com/questions/2256527/

Else statement crashes when i enter a letter for a cin << int value

Alright, I have a question, I veered away from using strings for selection so now I use an integer. When the user enters a number then the game progresses. If they enter a wrong character it SHOULD give the else statement, however if I enter a letter or character the system goes into an endless loop effect then crashes. Is there a way to give the else statement even if the user defines the variable's type.
// action variable;
int c_action:
if (c_action == 1){
// enemy attack and user attack with added effect buffer.
///////////////////////////////////////////////////////
u_attack = userAttack(userAtk, weapons);
enemyHP = enemyHP - u_attack;
cout << " charging at the enemy you do " << u_attack << "damage" << endl;
e_attack = enemyAttack(enemyAtk);
userHP = userHP - e_attack;
cout << "however he lashes back causing you to have " << userHP << "health left " << endl << endl << endl << endl;
//end of ATTACK ACTION
}else{
cout << "invalid actions" << endl;
goto ACTIONS;
}
You haven't shown how you are reading the integer. But in general you want to do something like this:
int answer;
if (cin >> answer)
{
// the user input a valid integer, process it
}
else
{
// the user didn't enter a valid integer
// now you probably want to consume the rest of the input until newline and
// re-prompt the user
}
The problem is that your cin is grabbing the character and then failing, which leaves the character in the input buffer. You need to check whether the cin worked:
if( cin >> k) { ... }
or
cin >>k;
if(!cin.fail()) { ... }
and if it fails, clear the buffer and the fail bit:
cin.clear(); // clears the fail bit
cin.ignore(numeric_limits<streamsize>::max()); // ignore all the characters currently in the stream
EDIT: numeric_limits is found in the limits header file, which you include as per usual:
#include <limits>
Your problem is not with the else-statement, but with your input. If you do something like
cin >> i;
and enter a character, the streams error state is set and any subsequent try to read from the stream will fail unless you reset the error state first.
You should read a string instead and convert the strings contents to integer.