Alright guys so I'm learning C++ using Bjarne Stroustrup's Programming Principles and Practice Using C++ book, and one of the drills asks me to
Step one: Enter a number followed by a unit(allow cm, m, in, and ft)
Step two: Convert that number into meters and output it
Step three: Reject any unit that the program doesn't know how to convert
Step four: Reject any values without units
Here is what I have so far:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
inline void keep_window_open() { char ch; cin>>ch; }
int main()
{
constexpr double cm_to_m = 100;
constexpr double cm_to_in = 2.54;
constexpr double in_to_ft = 12;
double i, in_meters;
string unit;
while (cin >> i)
{
cin >> unit;
if(unit == "cm" || unit == " cm") {in_meters = i / cm_to_m; cout << "That is " << in_meters << "m" << endl;}
else if(unit == "in" || unit == " in") {in_meters = i * cm_to_in / cm_to_m; cout << "That is " << in_meters << "m" << endl;}
else if(unit == "ft" || unit == " ft") {in_meters = (i * in_to_ft) * cm_to_in / cm_to_m; cout << "That is " << in_meters << "m" << endl;}
else if(unit == "m" || unit == " m") {cout << "That is " << i << "m" << endl;}
else if() cout << "You need to enter a unit after the number(cm, m, in, ft)" << endl;
else cout << "I don't know that unit" << endl;
}
}
My problem is at step four. I cannot, for the life of me, figure out how to check if no unit has been entered after the number. I've tried if(unit == " "), and everything similar to that, but when I just type in a number, it continues until I type in a unit.
If you guys can help with just giving hints so I can figure it out myself, that would be helpful :)
TIA
I think you could wait till there is a \n(end of line) and check if there is no unit
First off, you should always check whether input is successful after an attempt to read it. To do so, you'd check the stream state. Typically, that's done in combination with the read, e.g.:
if (std::cin >> unit) {
// ...
}
else {
// deal with no input being received
}
From the looks of it you want to constrain where the unit is located relative to the previous value: it seems you want the unit to be directly adjacent to the value or separated with at most one space. The input operator for std::string, like all well-behaved input operators, starts with skipping leading whitespace by default. If you don't want to allow arbitrary whitespace, you'll need to test for that explicitly.
When you want to allow at most one space character, you would need to deal with that explicitly. To do so you'd peek() at the next character and if it is a space (' ') you'd ignore() it. Next you can either disable skipping of leading whitespace using std::noskipws or peek() at the next character and fail if it is a whitespace character (std::isspace()). The next step would be reading the unit.
That is, reading the unit immediately after the value could look like this:
if (std::cin.peek() == ' ') {
std::cin.ignore();
}
if (std:cin >> std::noskipws >> unit >> std::skipws) {
// see if unit is a valid unit
}
else {
// no unit was entered where expected
}
The unit variable won't include a space character unless you read the string differently, e.g., using std::getline(). Thus, you can simply check the unit against "cm", "in", etc. There is no point to check it against " cm" for example as this test will always fail.
In addition I'd also get rid of the if () in your code: it serves no purpose other than adding confusion. I'm not even sure whether it is legal. gcc and clang both refuse to compile code containing an if-statement with an empty condition.
Finally, instead of keep_window_open() I'd just wait for some input using std::cin.ignore();. If you insist in keeping this function, its implementation could still be simplified to become std::cin.ignore();.
Related
The code in the cont function asks the user if they want to play my game again.
The code works when receiving proper character inputs such as 'y' or 'n' as well as their respective capital letter variants, and the else block works properly to loop the function if an invalid input such as 'a' or 'c' is entered.
However during a test run, an input of 'yy' breaks the code causing the program to infinitely loop, running not only this cont function but my game function as well.
choice is stored as a char variable. I am wondering why the code even continues to run upon inputting multi-character inputs such as 'yy' or 'yes'. What's interesting is 'nn', 'ny' and other variations of multi-character inputs that begin with 'n' causes no issues and properly results in the else if block running as intended. Which prints "Thanks for playing." then ends the program.
Can variables declared as char accept inputs greater than 1 character? Does it only take the first value? And if so why does 'yy' cause a loop rather than the program running as intended by accepting a value of 'y' or 'Y'? How can I change my program so that an input of 'yy' no longer causes issues, without specific lines targeting inputs such as 'yy' or 'yes'.
#include <iostream>
#include <string> // needed to use strings
#include <cstdlib> // needed to use random numbers
#include <ctime>
using namespace std;
// declaring functions
void cont();
void game();
void diceRoll();
// variable declaration
string playerName;
int balance; // stores player's balance
int bettingAmount; // amount being bet, input by player
int guess; // users input for guess
int dice; // stores the random number
char choice;
// main functions
int main()
{
srand(time(0)); // seeds the random number, generates random number
cout << "\n\t\t-=-=-= Dice Roll Game =-=-=-\n";
cout << "\n\nWhat's your name?\n";
getline(cin, playerName);
cout << "\nEnter your starting balance to play with : $";
cin >> balance;
game();
cont();
}
// function declaration
void cont()
{
cin >> choice;
if(choice == 'Y' || choice == 'y')
{
cout << "\n\n";
game();
}
else if (choice == 'N' || choice == 'n')
{
cout << "\n\nThanks for playing.";
}
else
{
cout << "\n\nInvalid input, please type 'y' or 'n'";
cont(); // calls itself (recursive function!!!)
}
}
void game()
{
do
{
cout << "\nYour current balance is $ " << balance << "\n";
cout << "Hey, " << playerName << ", enter amount to bet : $";
cin >> bettingAmount;
if(bettingAmount > balance)
cout << "\nBetting balance can't be more than current balance!\n" << "\nRe-enter bet\n";
} while(bettingAmount > balance);
// Get player's numbers
do
{
cout << "\nA dice will be rolled, guess the side facing up, any number between 1 and 6 : \n";
cin >> guess;
if(guess <= 0 || guess > 6 )
{
cout << "\nYour guess should be between 1 and 6\n" << "Re-enter guess:\n";
}
} while(guess <= 0 || guess > 6);
dice = rand() % 6+1;
diceRoll();
if (dice == guess)
{
cout << "\n\nYou guessed correctly! You won $" << (bettingAmount * 6);
balance = balance + (bettingAmount * 6);
}
else
{
cout << "\n\nYou guessed wrong. You lost $" << bettingAmount << "\n";
balance = balance - bettingAmount;
}
cout << "\n" << playerName << ", you now have a balance of $" << balance << "\n";
if (balance == 0)
{
cout << "You're out of money, game over";
}
cout << "\nDo you want to play again? type y or n : \n";
cont();
}
void diceRoll()
{
cout << "The winning number is " << dice << "\n";
}
Does it only take the first value?
Yes, the >> formatted extraction operator, when called for a single char value, will read the first non-whitespace character, and stop. Everything after it remains unread.
why does 'yy' cause a loop
Because the first "y" gets read, for the reasons explained above. The second "y" remains unread.
This is a very common mistake and a misconception about what >> does. It does not read an entire line of typed input. It only reads a single value after skipping any whitespace that precedes it.
Your program stops until an entire line of input gets typed, followed by Enter, but that's not what >> reads. It only reads what it's asked to read, and everything else that gets typed in remains unread.
So the program continues to execute, until it reaches this part:
cin >> bettingAmount;
At this point the next unread character in the input is y. The >> formatted extraction operator, for an int value like this bettingAmount, requires numerical input (following optional whitespace). But the next character is not numerical. It's the character y.
This results in the formatted >> extraction operator failing. Nothing gets read into bettingAmount. It remains completely unaltered by the >> operator. Because it is declared in global scope it was zero-initialized. So it remains 0.
In addition to the >> extraction operator failing, as part of it failing it sets the input stream to a failed state. When an input stream is in a failed state all subsequent input operation automatically fail without doing anything. And that's why your program ends up in an infinite loop.
Although there is a way to clear the input stream from its failed state this is a clumsy approach. The clean solution is to fix the code that reads input.
If your intent is to stop the program and enter something followed by Enter then that's what std::getline is for. The shown program uses it to read some of its initial input.
The path of least resistance is to simply use std::getline to read all input. Instead of using >> to read a single character use std::getline to read the next line of typed in input, into a std::string, then check the the string's first character and see what it is. Problem solved.
cin >> bettingAmount;
And you want to do the same thing here. Otherwise you'll just run into the same problem: mistyped input will result in a failed input operation, and a major headache.
Why do you need this headache? Just use std::getline to read text into a std::string, construct a std::istringstream from it, then use >> on the std::istringstream, and check its return value to determine whether it failed, or not. That's a simple way to check for invalid input, and if something other than numeric input was typed in here, you have complete freedom on how to handle bad typed in input.
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.
I'm new to this and currently studying C++. I'm currently learning about Character Functions in cctype(ctype). I'm having trouble understanding why the isspace(a_character) is not returning my cout message -- the problem is it wont even accept my Char user input. Any help or steering to the right direction would be greatly appreciated. I can achieve the response I want if I assign the Char value of: ' ' , however, it defeats the purpose. I've copied a portion of my code. To my understanding, there's no exact symbol for Whitespace? if so, is it possible to even enter a whitespace as an input? I've tried entering : ' ' however, have not been successful. Again, I'd greatly appreciate it.
#include <iostream>
#include <cctype>
#include <ctype.h>
using namespace std;
int main()
{
char c ;
char ans = 'y' || 'Y';
do {
cout << "Enter a character \n";
cin >> c;
if (isspace (c)) //Having trouble get this to actually produce a value
{
cout << "Your character " << c << "is a whitespace";
}
if (ispunct(c))
{
cout << c << " is a punctuation character\n";
}
cout << "Would you like to enter another value? \n";
cin >> ans;
} while (ans == 'Y' || ans == 'y');
return 0;
}
>> is a formatted extractor. Formatted extractors, by default, extract and discard all whitespace characters before they actually read anything. That's useful behavior, but not when you are trying to read a whitespace character.
Either use unformatted input (get()) or the noskipws manipulator. Note that your later reading of ans in all likelihood depends on skipping whitespace to work correctly, so you'd probably want to restore the whitespace-skipping behavior with skipws after you read c if you choose the second option.
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') {
// ...
}
Using GNU GCC Compiler 4.8.1. This really simple program has me stuck on something. When prompted for input, it expects an integer for the first value and a character for the second value. If I instead enter a character or any string of text instead of an integer, it always returns unit with a value of the character 'u'.
Can anyone explain why that is the case?
int main()
{
const double cm_per_inch = 2.54;
int length = 1;
char unit;
cout << "Please enter a length followed by a unit (c or i): ";
cin >> length;
cin >> unit;
if (unit == 'i')
cout << length << "in == " << cm_per_inch * length << "cm\n ";
else if (unit == 'c')
cout << length << "cm == " << length/cm_per_inch << "in\n";
else
cout << " I don't know a unit called " << unit << ".\n";
}
You don't initialize unit, so if the input fails, it contains whatever random, nonsense value it happened to have. None of your input code tests to see if it succeeds or fails, yet you access the value of unit either way. So the results if the input fails are unpredictable garbage.
Try changing:
char unit;
to
char unit = '?';
One of your issues is that you don't initialize your unit variable, so we have no idea what it will print out when your input fails.
You should really check for errors after each input and handle them then:
if (cin >> length)
{
if (cin >> unit)
{
// ...
}
}
Inputting of an integer will fail if it encounters a non-integer character.