How do I get keyboard input for a const* char parameter? - c++

I have an object that works with arrays of smaller objects. I am now creating a larger interface object that needs to collect input data and send it into a parameter of const* char. What code would I use to capture keyboard input of something like a 20 character title and be able to pass it into this parameter?
In Short:
How do you get keyboard input of a name and pass it into this:
void Insert(const char* t)
I am restricted to using the iostream, iomanip, cstring, cctype libraries
EDIT: You asked for the whole code, so here it is. All my input is having problems...
#include <iostream>
#include "store.h"
using namespace std;
void ShowMenu()
// Display the main program menu.
{
cout << "\n\t\t*** BOOKSTORE MENU ***";
cout << "\n\tA \tAdd a Book to Inventory";
cout << "\n\tF \tFind a book from Inventory";
cout << "\n\tS \tSell a book";
cout << "\n\tD \tDisplay the inventory list";
cout << "\n\tG \tGenre summary";
cout << "\n\tO \tSort inventory list";
cout << "\n\tM \tShow this Menu";
cout << "\n\tX \teXit Program";
}
char GetAChar(const char* promptString)
// Prompt the user and get a single character,
// discarding the Return character.
// Used in GetCommand.
{
char response;// the char to be returned
cout << promptString;// Prompt the user
cin >> response;// Get a char,
response = toupper(response);// and convert it to uppercase
cin.get();// Discard newline char from input.
return response;
}
char Legal(char c)
// Determine if a particular character, c, corresponds
// to a legal menu command. Returns 1 if legal, 0 if not.
// Used in GetCommand.
{
return((c == 'A') || (c == 'F') || (c == 'S') ||
(c == 'D') || (c == 'G') || (c == 'O') ||
(c == 'M') || (c == 'X'));
}
char GetCommand()
// Prompts the user for a menu command until a legal
// command character is entered. Return the command character.
// Calls GetAChar, Legal, ShowMenu.
{
char cmd = GetAChar("\n\n>");// Get a command character.
while (!Legal(cmd))// As long as it's not a legal command,
{// display menu and try again.
cout << "\nIllegal command, please try again . . .";
ShowMenu();
cmd = GetAChar("\n\n>");
}
return cmd;
}
void Add(Store s)
{
char* aTitle;
char aAuthor[21];
Genre aGenre = FICTION;
double aPrice = 10.00;
cout << "Enter title: ";
cin >> aTitle;
cout << "Enter author: ";
cin.getline(aAuthor, 20);
cout << aTitle << " " << "aAuthor\n";
s.Insert(aTitle, aAuthor, aGenre, aPrice);
}
void Find()
{
}
void Sell()
{
}
void Genre()
{
}
void Sort()
{
}
void Intro(Store s)
{
double amount;
cout << "*** Welcome to Bookstore Inventory Manager ***\n"
<< "Please input the starting money in the cash register: ";
cin >> amount;
s.SetCashRegister(amount);
}
int main()
{
Store mainStore;// Create and initialize a Store.
Intro(mainStore);//Display intro & set Cash Regsiter
ShowMenu();// Display the menu.
mainStore.Insert("A Clockwork Orange", "Anthony Burgess", SCIFI, 30.25);
mainStore.Insert("X-Factor", "Anthony Burgess", SCIFI, 30.25);
char command;// menu command entered by user
do
{
command = GetCommand();// Retrieve a command.
switch (command)
{
case 'A': Add(mainStore); break;
case 'F': Find(); break;
case 'S': Sell(); break;
case 'D': mainStore.DisplayStore(); break;
case 'G': Genre(); break;
case 'O': Sort(); break;
case 'M': ShowMenu(); break;
case 'X': break;
}
} while ((command != 'X'));
return 0;
}

Consider std::istream::getline(char *, std::streamsize).
But, be sure that you pass it a valid pointer to allocated memory! That is, use it like this:
char buffer[80];
std::cin.getline(buffer, sizeof buffer);
and not like this:
char *p;
std::cin.getline(p, 80); // Undefined behavior: using uninitialized variable
EDIT. You have this code:
char* aTitle;
...
cout << "Enter title: ";
cin >> aTitle;
This is a bug. You create a pointer called aTitle, and do not initialize it. That pointer now points at memory you do not own.
The >> operator writes data to the location pointed to by your pointer. Since your pointer does not point at anything you control, the >> will invoke undefined behavior when its writes through your pointer.
Lesson: make sure that you provide valid values for all of your pointers. (Broader lesson: never use pointers. (Okay, almost never.))
Immediately following, you have this code:
cout << "Enter author: ";
cin.getline(aAuthor, 20);
But, consider the input state. Your user just typed "Jaws", followed by ENTER. Your cin>>aTitle read "Jaws", and left "\n" in the input stream.
This istream::getline call reads up to the first newline, which is the newline that follows "Jaws"(!) and not the newline that follows "Peter Benchley"! So now, you have "Jaws" in aTitle (assuming you fix your previous bug), nothing in aAuthor, and "Peter Benchley\n" still in the input stream.
Lesson: Don't mix formatted input with getline. Use either one or the other consistently throughout your program.

Use getline to take a std::string input. And pass it to the function converting to a c-style string using member function c_str()

Use std::string and pass by reference.
You need a pointer to mutable data:
char * t; // Pointer to mutable data
OR
char* const t; // Constant pointer to mutable character.

Because of the weird restrictions (iostream is allowed, but not std::string), you might have a hard time finding your answer in a C++ text book. So here's some help:
char buffer[81];
std::cin >> buffer; // input some text (80 characters or less)
// A mutable character array can be implicitly converted
// to a const char* without casting.
Insert(buffer);

Related

why input causes problem to my do/while menu

I am trying to write a program with the menu using do/while.
As first step i am entering command line argument in parentheses, example "some text here", and get a C-style string. On each case my program should call a specific function on that string.
The problem is, that one of my functions using a user input cin.getline(). When I am calling it and trying to type in a new string to replace the previous one, my program takes this string as a choices in a menu.
The main function:
char newArray[100]{};
int choice{0};
do {
cin >> choice;
switch (choice) {
case 1:
functionF(argv[1], newArray);
break;
case 2:
cout << argv[1] << endl;
break;
default:
cout << "wtf";
}
} while (choice != 3);
And a function:
void functionF(char *ptr, char newArray[]) {
cin.getline(newArray, 100);
for (int i = 0; i < 100; ++i) {
ptr[i] = newArray[i];
if (newArray[i] == '\0')
break;
}
}
Without do/while menu function works fine.
Input example : command-line input "text sample",
Now argv[1] is this C-style string. If i type 2, the program outputs argv[1], when I press 1 I expect program to take another C-style string as user input. But when I am trying to input new string my program takes my input like (cin >> choice), for example I am trying to type "new text is here", and my program have to change argv[1] for this line but it takes this input as (cin >> choice).
void functionF(char *ptr, char newArray[]) {
cin.ignore(); //add this line to fix an error
cin.getline(newArray, 100);
for (int i = 0; i < 100; ++i) {
ptr[i] = newArray[i];
if (newArray[i] == '\0')
break;
}
}

C++ Debug Assertion Failed, Invalid Null Pointer

I've looked everywhere, but I cannot find a solution to exactly why this happens in my situation.
I'm making a simple string function that asks for a string, and prints out the length.
However, I get an "Invalid Null Pointer" assertion error when I run the compiled version. I have had no errors when compiling, but the error comes up when I run it.
This is the function causing the problem:
string getString()
{
string wordInput;
cout << "Enter a word that has AT LEAST four (4) letters! ";
getline(cin, wordInput);
while (wordInput.length() <= 3)
{
cout << "Enter a word that has AT LEAST four (4) letters! ";
getline(cin, wordInput);
}
return 0;
}
The while loop isn't a problem. I commented it out and I still got the same error. How is initializing word input, cout, and getline giving me the error?
Here is my whole code so far (not finished). I tried running the string by itself too, the getKeyLetter function isn't a problem.
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
char getKeyLetter()
{
char keyLetter;
string convertFromString;
cout << "Enter a SINGLE character! ";
getline(cin, convertFromString);
while (convertFromString.length() > 1)
{
cout << "Enter a SINGLE character! ";
getline(cin, convertFromString);
}
assert(convertFromString.size() == 1);
keyLetter = convertFromString[0];
return 0;
}
string getString()
{
string wordInput;
cout << "Enter a word that has AT LEAST four (4) letters! ";
getline(cin, wordInput);
while (wordInput.length() <= 3)
{
cout << "Enter a word that has AT LEAST four (4) letters! ";
getline(cin, wordInput);
}
return 0;
}
int main()
{
getKeyLetter();
getString();
return 0;
}
First, in your GetKeyChar() function, writing:
char ch;
cout << "Enter a single character: ";
cin >> ch;
will give you the first character the person types into the command prompt. So, typing "check" will have ch = c.
Second, as eran said, at the end of your functions, you have
return 0;
Unless you want both functions to return a char and string respectively, make them void GetKeyLetter() and void GetString(). Or, if you do want to return something, have them return ch (from my example) and return wordInput.
Only int main(), per standard, needs return 0, to show you that it exited correctly. the variable type you put in front of your functions is what variable you plan on returning. 0 is an int, so that's what it returns based on convention. As was pointed out, a return is not necessary in main. If you want your functions to return values, do this in your main.
string str;
char ch;
ch = GetKeyLetter();
str = GetString();
return 0;
And have your functions return the char and string value you want them to.

Switch-statement inside a while-loop which loop infinitely

Here is my code:
int main()
{
int nothing;
string name;
int classnum;
bool classchosen;
string classname;
cout << "Welcome adventurer, your journey is about to begin.\n\n";
cout << "Firstly, what's your name? ";
cin >> name;
classchosen = false;
while (classchosen == false)
{
cout << "\n\nNow, " << name << ", choose your class entering its number.\n\n";
cout << "1- Warrior\n" << "2- Mage\n" << "3- Paladin\n" << "4- Monk\n\n";
cout << "Class number: ";
cin >> classnum;
switch(classnum){
case 1:
classname = "Warrior";
classchosen = true;
break;
case 2:
classname = "Mage";
classchosen = true;
break;
case 3:
classname = "Paladin";
classchosen = true;
break;
case 4:
classname = "Monk";
classchosen = true;
break;
default:
cout << "\nWrong choice, you have to enter a number between 1 and 4.\n" << endl;
break;
}
}
cout << "\nSo you are a " << classname << " ? Well, tell me something more about you...\n";
cin >> nothing;
return 0;
}
Now, when I run it and input a string (for example "fjdfhdk") when it asks about the class number, the program loops infinitely instead of going in the default statement, writing again the question and letting me choose another class. Why?
Try something like this:
#include <sstream>
#include <string>
using namespace std;
int getInt(const int defaultValue = -1){
std::string input;
cin >> input;
stringstream stream(input);
int result = defaultValue;
if(stream >> result) return result;
else return defaultValue;
}
//..in main
cout << "Class number: ";
int classNum = getInt();
switch(classNum){ .... }
The reason why it fails in your case is because cin is trying to read a bunch of chars into a int variable. You can either read it as a string and convert as necessary, or you can check the cin state explicitly when reading into a int variable by checking if any of the fail bits are set. The fail bits would be set if for example you try to read bunch of chars into an int.
Because you're reading into an int, and the read fails. This
has two effects:
your use of classnum afterwards is undefined behavior, and
the stream has memorized the error condition, so you can
check it later.
As long as the error condition is not cleared, all further
operations on the stream are no-ops. The simplest changes in
your program to make this work would be:
std::cin >> classnum;
if ( !std::cin ) {
classnum = 0;
std::cin.clear();
std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
}
switch ( classnum ) // ...
In case of an error, this sets classnum to a known value,
clears the error state, and skips all input up to the next
newline. (Otherwise, you'll just fail again, because the
characters which triggered the error are still there.)
Consider, however, using a separate function to extract the int,
and using getline, as per user814628's suggestion. The above
is more to explain to you what is happening, and why your see
the symptoms you see. user814628's suggestion is far better
software engineering.

C++ cin only accept numeric values

I've written this piece of code that allows the user to choose input either the value 1 or 2. This is working perfectly fine aside from one minor issue:
If the user inputs something like "1asdaosd" the input is recognized only as 1.
I've tried using the isdigit function but I still didn't manage to make this work.
bool validInput;
do
{
cout << "Choose the game type: ";
cin >> gametype;
validInput = true;
if (cin.fail())
{
validInput = false;
cin.clear();
cin.ignore(std::numeric_limits<int>::max(), '\n');
}
if (gametype<1 || gametype>2) {
validInput = false;
}
} while (!validInput);
The expected behaviour should be:
Anything other than "1" or "2" shouldn't be considered a validInput and therefore repeating the cycle. What happens is that "1asdasd" or "2aods" is considered a validInput but I want it to fail.
Below is a method based on stuff I read in one of the early chapters of Stroustrup's Programming: Principles and Practice Using C++ and an answer provided by Duoas at cplusplus.com. It defines a function, get_int_between(), that allows you to do something like this:
int my_variable;
get_int_between(my_variable, min, max, prompt, error_msg);
Which would prompt, validate, and store into my_variable.
Just for fun, I've also included a function, get_int(my_variable, prompt, error_msg), that does the same thing but allows an integer of any value.
#include <iostream>
#include <sstream> // stringstream
void get_int(int& d, std::string prompt, std::string fail);
void get_int_between(int& d, int min, int max, std::string prompt, std::string fail);
int main()
{
int my_number = 1; // initialize my_number
get_int(my_number, "Please enter an integer: ", "Sorry, that's not an integer.\n");
//Do something, e.g.
std::cout << "You entered: " << my_number << "\n";
get_int_between(my_number, 1, 2, "Choose the game type (1 or 2): ", "Sorry, that's not an integer.\n");
//Do something, e.g.:
std::cout << "Let's play Game " << my_number << "!\n";
return 0;
}
void get_int(int& d, std::string prompt, std::string fail)
{
while(1) {
std::cout << prompt;
std::string str;
std::cin >> str;
std::istringstream ss(str);
int val1;
ss >> val1;
if(!ss.eof()) {
std::cout << fail;
continue;
} else {
d = val1;
break;
}
}
}
void get_int_between(int& d, int min, int max, std::string prompt, std::string fail)
{
while(1) {
get_int(d, prompt, fail);
if(d > max || d < min) {
std::cout << "Sorry, your choice is out of range.\n";
continue;
}
break;
}
}
If you want to use strings use getline.
#include <iostream> // std::cin, std::cout
int main ()
{
char name[256], title[256];
std::cout << "Please, enter your name: ";
std::cin.getline (name,256);
std::cout << "Please, enter your favourite movie: ";
std::cin.getline (title,256);
std::cout << name << "'s favourite movie is " << title;
return 0;
}
if you make gametype as an int it will only accept 1 or 2 (of course you have to prevent other numbers to be accepted).
It's because gametype is an integer, so it's trying to read as much as would be valid for an integer. 1asdaosd is not a valid integer so it stops at the 1. If you want to read that thing in completely you'll have to make gametype a string for example, but then you won't be able to compare it to integers as you already do.
You can read it as a string if you want, and if you want to handle the case of strings and ints both, then you can use something like stoi to attempt to convert the string to an integer. Then catch the std::invalid_argument exception so you can know if the string can be converted to an integer. If it can't, then you know to keep it as a string.
It reads an int as far the input can be construed as such. Then stops. If you read into a string variable it will get it all.
Read data into a string variable.
Check that data is a valid integer.
Convert string to integer.
Tedious but it's the only way to do it
I'm guessing you want one input value on each line. You need to read this as string and then check if you got more than you asked for. If you need it as an integer you can convert the read string later.
I'm also assuming you only need to read single digit integers. More digits need the string to integer conversion in the loop and some more checks.
string gametype;
do
{
cout << "Choose the game type: ";
// read one word as string, no conversion, so will not fail (may hit eof though)
cin >> gametype;
// ignore rest of line (assuming you want next valid input on next line)
cin.ignore(std::numeric_limits<int>::max(), '\n');
}
while ( gametype.size() != 1 || gametype.at(0) < '1' || gametype.at(0) > '2') );
// char to int conversion (single digit only)
int gametypeint = gametype.at(0) - '0';
// other way to convert string to int
istringstream iss(gametype);
iss >> gametypeint;
// yet another way (C++11)
gametypeint = stio(gametype);

What's wrong with the following C++ code involving input and string?

#include <iostream>
typedef int temperature;
temperature celsiustemperature[4];
int main()
{
using namespace std;
cout << "Enter a start temperature in celsius: ";
cin >> celsiustemperature[0];
cout << "Enter an end temperature in celsius: ";
cin >> celsiustemperature[1];
cout << "You printed " << celsiustemperature[0] << " and " << celsiustemperature[1] << "." << " Is this correct?" << endl;
char szYesOrNo;
cin >> szYesOrNo;
switch (szYesOrNo)
{
case "yes":
cout << "win";
break;
case "no":
cout << "winner";
break;
}
return 0;
}
I cannot figure out what is wrong with the following code. Sorry for not adding comments; I want it to print win if the user inputs yes and winner if user inputs no.
This won't compile, because szYesOrNo is a char, and you are comparing it with string literals in your switch statement. String literals are of type const char[], which cannot be directly compared to char.
Use std::string instead of a char:
std::string szYesOrNo;
This will also force you to remove the switch, because switch cannot operate on a string value (also notice, that your switch does not have a default case, so it won't handle incorrect input). Just do it this way:
if (szYesOrNo == "yes")
{
cout << "win";
}
else if (szYesOrNo == "no")
{
cout << "winner";
}
else
{
// Handle wrong input...
}
You can't switch on a string. Only on an integer-like type (ints, enums, chars, longs).
The main problem with your code is that you declare a variable as char szYesOrNo; which can only hold ONE symbol, such as a letter, but then expect the character to enter a whole word. You should use a string to do this instead. When you fix that, you will need to use if statements instead of a switch statement to make decisions.
First of all, szYesOrNo is a single char and so can only contain one character. When you do cin >> szYesOrNo;, you are only reading y or n.
Second, you're trying to use a switch statement to compare this single char to the string literals "yes" and "no". This comparison doesn't make sense. The string literals are of type "array of N const char".
Instead, use std::string like so:
string szYesOrNo;
cin >> szYesOrNo;
if (szYesOrNo == "yes") {
cout << "win";
} else if (szYesOrNo == "no") {
cout << "winner";
}
szYesOrNo is a character, so you cannot switch it in a case of "yes" which is a string ( 3 characters )
char szYesOrNo;
This is a single character.
switch (szYesOrNo)
{
case "yes":
This will compare the value of the single character with the address of the string "yes", which isn't what you want.
Either input to a string, or compare chars.
To compare chars:
switch (szYesOrNo)
{
case 'y':
To compare strings, you can't use a switch statement. You could use nested if/else:
string szYesOrNo;
if(szYesOrNo == "yes") {
cout << "win";
} else if(szYesOrNo == "no") {
cout << "winner";
}
You can use == when comparing a string to a char[] (which is what "yes" is). string makes sure the contents are compared rather than just the addresses.