How to limit user input in C++ for strings & characters? - c++

I'm trying to create a small restaurant program in which I'll be practicing everything I learned in C++ so far. However I jumped into a small issue. At the beginning of the program, I prompt the user whether they want to enter the program, or leave it by choosing Y or N. If the input is anything other than that the program will tell the user is invalid.
The issue is lets say the user input one invalid character a.
The invalid output will be displayed normally and everything seems perfect.
But if the user inputs two characters, or more, the invalid output case will be printed as many as the characters input by the user. Sample below:
Output image
#include <iostream>
int main()
{
char ContinueAnswer;
std::string Employee {"Lara"};
std::cout << "\n\t\t\t---------------------------------------"
<< "\n\t\t\t| |"
<< "\n\t\t\t| Welcome to OP |"
<< "\n\t\t\t|Home to the best fast food in Orlando|"
<< "\n\t\t\t| |"
<< "\n\t\t\t--------------------------------------|" << std::endl;
do
{
std::cout << "\n\t\t\t Would you like to enter? (Y/N)"
<< "\n\t\t\t "; std::cin >> ContinueAnswer;
if(ContinueAnswer == 'y' || ContinueAnswer == 'Y')
{
system("cls");
std::cout << "\n\t\t\t My name is " << Employee << "."
<< "\n\t\t\tI will assist you as we go through the menu." << std::endl;
}
else if(ContinueAnswer == 'n' || ContinueAnswer == 'N')
{
std::cout << "\t\t\t\tGoodbye and come again!" << std::endl;
return 0;
}
else
std::cout << "\n\t\t\t\t Invalid Response" << std::endl;
}
while(ContinueAnswer != 'y' && ContinueAnswer != 'Y')
Thank you for taking time to read and for anyone who answers :)

You could simply make the user input a string:
std::string ContinueAnswer;
and compare like this:
if(ContinueAnswer == "y" || ContinueAnswer == "Y")
which will handle multi-character inputs.
If you want to handle spaces in the input as well, change the:
std::cin >> ContinueAnswer;
to:
std::getline(std::cin, ContinueAnswer);

Before addressing your question I need to point out that you should always verify that the input was successful before doing anything with it. Processing variables which were not set due to the inout failing is a rather common source of errors. For example:
if (std::cin >> ContinueAnswer) {
// do something with successfully read data
}
else {
// deal with the input failing, e.g., bail out
}
I assume you consider everything on the same line to be invalid if nine of the expected characters was read. You could read a line into an std::string. However, that could be abused to provide an extremely long line of input which would eventually crash your program. Also, reading data into a std::string just to throw it away seems ill-advised. I’d recommend ignoring all characters up to and including a newline which could be done using (you need to include <limits> for this approach):
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), ‘\n’);
The first argument is a special value indicating that there may be an arbitrary amount of character before the newline. In practice you could probably use a value like 1000 and it would be fine but it can be gamed. Of course, in a real application a dedicated limit may be used to prevent an adversary to keep the program busy for long. I tend to assume my programs are under attack to make sure I deal with unusual cases.

A quick refactor produces this:
#include <iostream>
#include <cstring>
#include <stdio.h>
int main()
{
char ContinueAnswer[256];
std::string Employee {"Lara"};
std::cout << "\n\t\t\t---------------------------------------"
<< "\n\t\t\t| |"
<< "\n\t\t\t| Welcome to OP |"
<< "\n\t\t\t|Home to the best fast food in Orlando|"
<< "\n\t\t\t| |"
<< "\n\t\t\t--------------------------------------|" << std::endl;
do
{
std::cout << "\n\t\t\t Would you like to enter? (Y/N)"
<< "\n\t\t\t "; std::cin.getline(ContinueAnswer,sizeof(ContinueAnswer));
if(strcmp(ContinueAnswer, "Y") == 0 || strcmp(ContinueAnswer, "y") == 0)
{
system("cls");
std::cout << "\n\t\t\t My name is " << Employee << "."
<< "\n\t\t\tI will assist you as we go through the menu." << std::endl;
}
else if(strcmp(ContinueAnswer, "N") == 0 || strcmp(ContinueAnswer, "n") == 0)
{
std::cout << "\t\t\t\tGoodbye and come again!" << std::endl;
return 0;
}
else
std::cout << "\n\t\t\t\t Invalid Response" << std::endl;
}
while(true);
}
The cin.getline will get all characters until a delimiter. Then, you can check for equivalence using strcmp and reject anything other than what you want. Lastly, it seems like you are wanting this to be in an infinite loop, so don't worry about checking the input at the end and just loop back.

Related

How do I make it so my program doesn't loop when adding new entries to a txt file

I am not sure where I put the char command declaration where I wont get a "Not defined in this scope" error and it will loop through accepting a new char command the next time the program loops.
I tried putting it inside of the do loop but then it said that char was not defined in that scope, I then put it right after the int main function and when entering A as a command it infinitely loops my add_entry function without allowing user input.
Do I have to pass by reference maybe? Or pass by value?
My file that holds all function definitions
#include "main.h"
using namespace std;
int syntax::add_entry()
{
cout << "Enter a concept name: ";
cin.get(name, SIZE);
cout << endl << "Enter an example of the syntax: ";
cin.get(example,SIZE);
cout << endl << "Enter a description of the syntax: ";
cin.get(desc,SIZE);
cout << endl << "Enter a difficulty rating from 1-10: ";
cin.get(diff,SIZE);
cout << endl << "Enter a usefulness rating from 1-10: ";
cin.get(use,SIZE);
//open and write to the file
ofstream myfile;
myfile.open("data.txt");
myfile << "Name: " << name << endl;
myfile << "Example of syntax: " << example << endl;
myfile << "Description of syntax: " << desc << endl;
myfile << "Difficulty rating from 1-10: " << diff << endl;
myfile << "Usefulness rating from 1-10: " << use << endl;
myfile.close();
return 0;
}
int syntax::display_entry()
{
ifstream myfile("data.txt");
/*
char name[SIZE];
char example[SIZE];
char desc[SIZE];
char diff[SIZE];
char use[SIZE];
*/
if(myfile.is_open())
{
while(myfile >> name >> example >> desc >> diff >> use)
{
std::cout << name << ", " << example << ", " << desc << ", " << diff << ", " << use;
}
myfile.close();
}else
cout << "File is not open" << endl;
std::cin.get();
return 0;
}
my main .cpp file
#include "main.h"
using namespace std;
int main()
{
char command;
syntax c;
do{
cout << "Welcome to the C++ concept syntax user database." << endl;
cout << "Choose one of the following commands: " << endl;
cout << endl << endl;
cout << "A) Add a new entry B) Display all entrys C) Search for difficulty D) Exit: ";
cin >> command;
cout << endl;
if(command == 'A' || command == 'a')
{
c.add_entry();
}
else if(command == 'B' || command == 'b')
{
c.display_entry();
}
else if(command == 'D' || command == 'd')
{
cout << "Quitting program, Thank you for using" << endl;
}
}while(command != 'D' || command != 'd');
return 0;
}
my .h file
#include <iostream>
#include <fstream>
#include <cstring>
#include <cctype>
using namespace std;
class syntax
{
public:
const static int SIZE = 50;
char name[SIZE];
char example[SIZE];
char desc[SIZE];
char diff[SIZE];
char use[SIZE];
int display_entry();
int add_entry();
private:
};
You need to read and understand about "formatted input" and "unformatted input". Please check here.
Formatted input is done using the extractor operator >>. It reads characters from a stream and formats them to the expected variable type. If you write int x; std::cin >> x and you enter the number 12, so the digits/characters '1' and '2', your input will be formatted / converted to an integer value 12.
It is important to understand that formatted input
ignores leading white space
stops any conversion when encountering white space (but does not extract it from the stream)
Meaning, if you enter 12 and then press the enter-key, the characters '1' and '2' will be extracted from the stream, but the newline 'n' will not be consumed or extracted from the screen and is still available.
This default behavior can be addapted by setting certain flags.
Now, if we look at "unformatted input" functions, like get, it will read all kind of characters, including spaces and so on until it hits the specified delimiter, which is '\n' per default. For the get function, the delimiter '\n' will not be extracted. So, it is still in the stream. This is in contrast to the getline function which would extract the '\n' from the stream (but not store it).
All this you can read in the linked description.
And now, the root cause for all you problems, is also written in the description:
If no characters were extracted, calls setstate(failbit)
Then, let us look on the order of events
You enter a 'a', becuase you want to add an entry
The 'a' will be extracted and the '\n' is still in the stream
In function "add_entry" you call "get"
Get will try to read charcters, until it finds a newline '\n'
But, as a leftover from the previous operation, it will immediately see the '\n' , and hence store no other data at all, and consequently sets the failbit of the stream. All the following calls to std::cin will do nothing, because the failbit of the stream is set.
The functions returns to main and the failbit is still set
The next call cin >> command; will do nothing and will especially not modify the "command" variable. This will still contain an 'a'
And then the loop runs forever
You have an additional bug in the "while" condition. This must be corrected to: ´while (command != 'D' && command != 'd');´
Now, what to do.
First, and very important, for any IO-function you need to check, if it worked or not. There are functions to read the iostate of the stream. But c++ makes life easier. The bool-operator and the not-operator are overwritten and will return state information. If you want to know, if any IO operation was successful, the you can write something like if (std::cin) ....
Very convenient. But must be used.
And since IO operations return mostly a reference to the stream for which they were called, you can write if (cin >> command) . . . . This will first call the extraction operator. This will return a reference to the stream and for that you can use an if statement, because of the overwritten bool-operator.
But how to overcome the nasty problem with the '\n' in the stream, which is often there? There are basically 2 functionalities:
Function ignore. Will ignore all/a number of characters, until a delimiter is hit.
Function/manipulator std::ws. Will eat all white spaces.
I recommend to add one time cin >> std::ws; at the top of your "add_entry" routine and then you must change all get functions to getline. If not, you would need to add std::ws before each get statement.
And again, for each IO function, check the status! For example if (!cin.getline(example, SIZE)) .... do something, show error
And in the future. For any transition from formatted to unformatted input, use std::ws
And, do never forget to read the documentation carefully.
Have fun!

Safe [Y/N]; [1/2/3/etc.] function

I tried to make a an introduction to a "game", and in its functions I made some Yes/No, 1/2/3, situations.
Im new to this however it wasn't that difficult, worked perfectly. The problem appeared when handling with invalid inputs. So this is what the code looks like by now:
#include "Introduction.h"
#include "GameConstants.h"
#include "PlayerCharacter.h"
#include <iostream>
#include <windows.h>
using namespace std;
Introduction::Introduction()
{
}
/////////Function N.1///////////
void Introduction::presentation()
{
char confirm;
string enteredName;
cout << constants.line() << "Welcome traveler! What is the name?" << endl;
getline(cin,enteredName);// Gets the WHOLE LINE as the name.
while (confirm != 'Y') //If the player doesn't confirm the name with 'Y' in will run again until it does.
{
cout << constants.xline() << "Your name is " << enteredName << " right? (Y/N)" << endl;
cin >> confirm; //The player's answer
cin.sync(); //Only takes the first character
confirm = toupper(confirm); //Turns player message into CAPS for easier detection in the "if" statements
if (confirm == 'N'){ //If not the correct name, gives another chance
cout << constants.xline() << "Please, tell me your name again..." << endl;
cin >> enteredName;
cin.sync();}
if ((confirm != 'Y')&&(confirm != 'N')){ //If an invalid input is entered, gives another chance. And insults you.
cout << constants.xline() << "Fool Go ahead, just enter your name again." << endl;
cin >> enteredName;
cin.sync();}
}
if (confirm == 'Y'){ //When the answer is yes ('Y') /* Uneeded line */
PC.setName(enteredName); //Saves the name
cout << constants.xline() << "Excellent! I have a few more questions for you " << PC.name() << "..." << endl;
}
}
//////////Function N.2///////////
void Introduction::difSelection(){
int selectedDif = 0; //Variable to store selected difficulty whitin this function.
Sleep(2500);
cout << constants.xline() << "What kind of adventure do you want to take part in?" << endl;
Sleep(2500); //Wait 2,5 s
cout << "\n1= Easy\n2= Normal\n3= Hard" << endl;
while(selectedDif != 1&&2&&3){ //Selected option must be 1/2/3 or will run again
cin >> selectedDif; //Sets the user selected difficulty
cin.sync(); //Gets only first character
if((selectedDif != 1||2||3)&&(!(selectedDif))){ //If the input isn't 1/2/3 AND is an invalid character, this will run. And it'll start again
cout << constants.xline() << "Criminal scum. Go again." << endl;
cin.clear();
cin.ignore();
}
if(selectedDif != 1&&2&&3){ //If selected option isn't 1/2/3, this will run and will loop again. However I know this conflicts with the previous statement since this will run anyways.
cout << constants.xline() << "Wrong input, please try again." << endl;
}
else if(selectedDif == 1){
constants.setDiff(1);
constants.setStatPoints(15);
} else if(selectedDif == 2){
constants.setDiff(2);
constants.setStatPoints(10);
} else if (selectedDif == 3){
constants.setDiff(3);
constants.setStatPoints(5);}
}
}
The first function works perfectly you can type "aaa" or "a a a" and will work. However I'd like to know if there's a simpler way to do it. (Understandable for a beginner, just started 3 days ago lol; if it includes some advanced or less known code prefer to stay like this by now).
Now, the second one, I really have no idea how to fix it. I need something that if the user's input was an invalid character type, throw certain message, and if it's an int type, but out of the range, another message. And of course, run again if it fails. Did a lot of search and couldn't find anything that meet this requirements.
To check if the user input is an int, you could use the good() function.
int val;
cin >> val;
if( cin.good() ) {
// user input was a valid int
} else {
// otherwise
}
As for the range check, the syntax is a bit different.
This returns true if the number is not equal to 1 nor 2 nor 3:
selectedDif != 1 && selectedDif != 2 && selectedDif != 3
Another shorter way would be to use:
selectedDif < 1 || selectedDif > 3
Another thing, in c++, there are two keywords break and continue which will allow to reduce the code in the loops.

I am trying to end my C++ program when the user presses 'Ctrl-D'

my program has read in a large file of words (a dictionary) and inserted them all into a hash table. I am prompting the user to lookup a word and I want them to be able to terminate the program by pressing Ctrl-D. This is what I have tried but when I press Ctrl-D it just gets stuck in a loop printing out what I have in the else statement. I am using Unix. I have attempted looking this up on this website and nothing was working that I was trying hence why I am asking my own question. Any thoughts? PS. The transform is to make the user input all uppercase to match the file I am reading from.
void query(){
bool done = false;
string lookupWord;
while(!done){
cout << "Type a word to lookup or type ctrl-D to quit: ";
cin >> lookupWord;
if(atoi(lookupWord.c_str()) == EOF)
done = true;
else{
transform(lookupWord.begin(), lookupWord.end(), lookupWord.begin(), ::toupper);
cout << endl << "Word: " << lookupWord << endl;
cout << endl << "Definition: " << myDict.lookup(lookupWord) << endl << endl;
}
}
}
atoi(lookupWord.c_str()) == EOF
This doesn't do what you think it does. This checks the return value of atoi() and compares it to EOF. EOF is typically defined as -1. So, the code ends up setting done only when -1 is typed in. Which is not what you want.
std::istream has a convenient operator bool that tests whether the file stream is in a good state. So, all that really needs to be done is:
if (cin >> lookupWord)
{
// Your existing code is here
}
else
{
done=true;
}

(C++) Trouble excluding alphanumeric input into application

so this is one of the first bits of code I am trying to write without too much direction, however I seem to have hit a wall. I am attempting to write a very basic "MPG" application and one thing I have come to find is that when the application asks for user input it allows inputs such as "2d" or any alphanumeric input and it continues to operate as long as the digit is first. For example "2d will work but "d2" will not, and the application will carry on as if the letter is not there. ex. 2d/2=1. Here's the code.
#include <iostream>
#include <ctype.h>
int main()
{
float a, b;
char again = 'Y';
std::cout << "After several hours on the road you wonder what your gas mileage must have been..." << "\n";
while (again == 'y' || again == 'Y')
{
std::cout << "How much gas did you have in your tank to start with?" << "\n";
while (!(std::cin >> a))//cin for float a
{
std::cout << "Your input must be a number...1" << "\n";
std::cin.clear();
std::cin.ignore(1000000, '\n');
}
if (a > 0)
std::cout << "How many miles did you travel?" << "\n";
while (!(std::cin >> b))//cin for float b
{
std::cout << "Your input must be a number...2" << "\n";
std::cin.clear();
std::cin.ignore(1000000, '\n');
}
if (b > 0)
std::cout << "You have obtained an whopping " << b / a << " miles to the gallon!" << "\n" << "\n";
std::cout << "Would you like to try again? (Y/N): ";
std::cin >> again;
}
}
By default, numeric formatting starts with skipping whitespace and stops reading characters as soon as one character not matching the format of the read type is encountered. If a number could be read before such a character is encountered the read succeeds.
That applied to your example of entering "2d" means that 2 is successfully read as number and the next character to be read is d. If you want to catch that situation as an error, you can check what the next character in the stream is. If you require individual numbers to be read on lines without any spaces trailing the value, you can simply do something like this:
while (!(std::cin >> a) || std::cin.peek() != '\n') {
// ...
}

While statement won't stop

I have a while statement that keeps repeating the text without giving the user a chance to input another value for action. What am I doing wrong? It still doesn't ask for input. I need for the code to display the text once, then ask for input. Presumably, if you typed anything but 1 it would repeat the sequence. But as it stands it simply kicks you out of the loop without the chance to correct the action (As of the last edit, see below.)
int action = 0;
while (action != 1)
{
cout << " No you must look it might be dangerous" << endl;
cin >> action;
}
One suggestion was:
while (action != 1)
{
cout << " No you must look it might be dangerous" << endl;
cin >> action;
cin.ignore();
}
That still produces text over and over.
while (action != 1)
{
cout << " No you must look it might be dangerous" << endl;
if (!(cin >> action))
// ...problems in the I/O stream...
break;
}
This one kicks you out without a chance to input a new action.
If you type a character that is not white space and can't be part of an integer, then you have an infinite loop. Each attempt to input to action fails on the invalid character without changing the value stored in action.
You could write:
int action = 0;
while (action != 1)
{
cout << " No you must look it might be dangerous" << endl;
if (!(cin >> action))
// ...problems in the I/O stream...
break;
}
This will handle EOF and alphabetic characters more gracefully than a continuous loop. You might need to set a flag or return an error condition from the function or do something else other than break out of the loop. Always check your inputs for success.
You might also consider outputting the value you're getting stored in action in the loop, so you can see what is happening:
int action = 0;
while (action != 1)
{
cout << " No you must look it might be dangerous" << endl;
if (!(cin >> action))
// ...problems in the I/O stream...
break;
cerr << "Action: " << action << endl;
}
This might tell you something useful too.
Please show a complete little program that illustrates your problem — an SSCCE (Short, Self-Contained, Correct Example).
For example, I'm testing with:
#include <iostream>
using namespace std;
int main()
{
int action = 0;
while (action != 1)
{
cout << " No you must look it might be dangerous" << endl;
if (!(cin >> action))
{
// ...problems in the I/O stream...
break;
}
cout << "Action: " << action << endl;
}
cout << "After loop" << endl;
if (!cin)
cout << "cin is bust" << endl;
else
cout << "Action: " << action << endl;
}
That's no longer minimal code — the material after the loop is merely telling me what is happening. But it does help me ensure that my code is doing what I expect.
What does your equivalent code look like, and what are you typing in response to the prompts — and especially, what are you typing before you get to this code fragment (and what other input activity is going on before you get here)?