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;
}
}
Related
So this is my code:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
long int iterFunc(int);
long int recurFunc(int);
int main() {
int n;
while(true){
try{
cout << "Enter: ";
if (!(cin >> n))
throw("Type Error");
if (n < 0)
throw n;
else
if (n == 0)
break;
cout << "Iterative: " << iterFunc(n) << endl;
cout << "Recursive: " << recurFunc(n) << endl;
}
catch(int n){
cout << "Error. Enter positive number." << endl;
}
catch(...){
cin.clear();
cin.ignore(100, '\n');
cout << "Error. Please enter a number" << endl;
}
}
cout << "Goodbye!";
return 0;
}
long int iterFunc(int n){
vector<long int> yVec = {1, 1, 1, 3, 5};
if (n <= 5)
return yVec[n - 1];
else
for(int i = 5;i < n; i++){
long int result = yVec[i - 1] + 3 * yVec[i- 5];
yVec.push_back(result);
}
return yVec.back();
}
long int recurFunc(int n){
switch (n) {
case 1:
case 2:
case 3:
return 1;
break;
case 4:
return 3;
break;
case 5:
return 5;
break;
default:
return recurFunc(n - 1) + 3 * recurFunc(n - 5);
break;
}
}`
The program shoud accept only one integer and return the y of the function using both iterative and recursive implemetations. Ex.: 30, 59, 433. How can I throw an error message if the user enters more then one integer, separated by space? Ex.: '3 45 32'.
I tried using if (cin.getline == ' ') throw("Error name") but the program still executes and return the y of the function for number in the input
Something like this works:
int main()
{
std::string str;
std::cout << "? : ";
std::getline(std::cin, str);
std::string::size_type pos(0);
int i = std::stoi(str, &pos);
if (pos != str.length())
return 1;
}
I found a part of my old code that might come in handy.
int val;
do
{
cin>>val;
if(!cin){ //you can add more conditions here
cin.clear();
cin.sync();
/* additional error handling */
}
else{
break; //input is correct - leaving loop
}
}while(true); //or here
Basically what !cin does is - it checks what type of value you actually want to write to, because it's needed anyway to figure out if data type is written to the correct type of our val. This means, that "30" or "433" etc. are integers (correct), "s" or "string" etc. are strings (or char*, correct me if I am wrong) (incorrect).
This also means, that "3 45 32" should be interpreted as string, which should result in another loop run.
Note: I didn't really test this code, so it might be completely wrong.
Edit: Okay now after some tests I realised this code needs some retweaking.
Firstly, "3 45 32" is not interpreted as string (now understandable). Instead, first number (before whitespace) is saved as an integer and all other numbers are stored in the buffer (next cin will be filled with it), which we can avoid using cin.clear() and cin.sync() once again.
The question is - is it okay for you to accept the first integer and ignore everything after the first whitespace? If not, you will have to save the input as string and extract whatever data you want from it.
I am leaving the original answer as is for simplicity of finding references in this edit.
This is only a small part from my code. What I'm trying to do is writing at the end of the file (add record) which in this case is "books.txt" that already has 40 records. But when I debug, it would still prompt the user to enter isbn code but after entering, (process 3296) exited with code 3. came out. Which part am I doing wrong? The counter() function is to count how many records I already have in my file. And I'm also using array of struct to store my records.
int add_record(DATA book[])
{
int count = counter();
system("CLS");
cout << "\t\t\t\t\t\t\t\t : :Add Book Record: :\n\n";
bool cont;
ofstream outfile("books.txt", ios::app);
if (outfile.is_open() && !outfile.eof())
{
do
{
cont = true;
cout << "ISBN Code: ";
cin.getline(book[++count].isbn_code, 14, '\n');
//cin.ignore(numeric_limits<streamsize>::max(), '\n');
int length = strlen(book[++count].isbn_code);
for (int i = 0; i <= length; i++)
{
if (!isdigit(book[++count].isbn_code[i]))
{
cont = false;
cout << "Your input is invalid. Enter again.\n";
break;
}
}
} while (cont == false);
do
{
cont = true;
cout << "Author: ";
cin.getline(book[++count].author, 50, '\n');
int length = strlen(book[++count].author);
for (int i = 0; i <= length; i++)
{
if (isdigit(book[++count].author[i]))
{
cont = false;
cout << "Your input is invalid. Enter again.\n";
break;
}
}
} while (cont == false);
outfile << book[++count].isbn_code << "," << book[++count].author ;
outfile.close();
}
else
cout << "File is not open\n";
return 0;
}
Yes, the error message is completely correct. This is a rare case where using a cast is the correct thing to do
if (isdigit(static_cast<unsigned char>(book[++count].author[i])))
Reference, https://en.cppreference.com/w/cpp/string/byte/isdigit
But this has nothing to do with your crash which is caused by other errors. For instance
cin.getline(book[++count].isbn_code, 14, '\n');
//cin.ignore(numeric_limits<streamsize>::max(), '\n');
int length = strlen(book[++count].isbn_code);
You definitely don't want to increment count twice. I would guess the correct code is
cin.getline(book[count].isbn_code, 14, '\n');
int length = strlen(book[count].isbn_code);
and to increment count once later in your loop.
Remember ++count is not the same as count + 1. The first increments the count variable, that is it changes the value of the count variable, but count + 1 just adds one to count and does not change the value of the count variable.
This is also wrong
for (int i = 0; i <= length; i++)
In C++ string indexes start at zero and go upto the length of the string minus one, so the correct code is
for (int i = 0; i < length; i++)
Also not part of your question but X can be a legal character in an ISBN.
I want a user to enter a char. I want to filter what they enter and take only the first char they type.
int main(){
while (true){
char n = readOption();
cout << n << std::endl;
}
return 0;
}
char readOption() {
char input = '\0';
while (input != '\n') {
input = cin.get();
if (isalpha(input)) {
break;
}
}
return toupper(input);
}
If I enter 13# jkjoi, the console prints.
J
K
J
O
I
I only want it to print J. Why is it printing the other letters as well?
It is printing all of the characters because (after you fix your semi-colon error) you loop forever:
while (true)
{
char n = readOption();
cout << n << std::endl;
}
This will call your read function over and over, forever! Your read function loops until he gets an alpha character, so it ignores "13# " and then grabs 1 character for each iteration of the while (true) loop. If you want it to stop after reading the first alpha character, don't loop:
char n = readOption();
cout << n << std::endl;
Updated
With your comment, you can actually re-write your code entirely:
std::locale loc;
char c = '\0';
do
{
// get a character with error checking
while (!(std::cin >> c))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
} while (!std::isalpha(c, loc));
// ignore the rest of the input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Because you asked it to.
You perform this in a loop, forever.
If you only want to do it once, then simply do it once. Remove your loops.
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);
Here's the code :
cout << "Please enter the file path: ";
string sPath;
getline(cin, sPath);
cout << "Please enter the password: ";
string sPassword; getline(cin, sPassword);
Problem is, when I run it it displays "Please enter the file path: " then it displays "Please enter the password: " and then waits for the password. It seems to completely skip the first 'getline()'.
Later edit: Yes there are some input operations done before.
int iOption = 0;
while (iOption == 0)
{
cout << "(E/D): ";
switch (GetCH())
{
case 'E':
iOption = 1;
break;
case 'e':
iOption = 1;
break;
case 'D':
iOption = 2;
break;
case 'd':
iOption = 3;
break;
default:
break;
}
}
And the code for GetCH() in case anyone asks.
char GetCH ()
{
char c;
cin >> c;
return c;
};
It looks like the rest of the line that was input for GetCH still remains in the buffer at the time that you call getline, i.e. at least a \n and this is what you are reading in the first getline call. The program doesn't block waiting for user input because the getline request can be satisfied by the partial line still queued for reading.
Consider modifying your GetCH function to read whole lines as well.
E.g. something like (totally untested, I'm afraid):
int GetCH()
{
std::string inputline;
// Read until error or we receive a non-empty line
while( std::getline(std::cin, inputline) && inputline.empty() )
{
}
return inputline.empty() ? EOF : inputline[0];
}
You need to clear whatever available in input stream like below
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max())
I have a cin.clear() before the while loop and have modified the GetCH option to get a whole string with 'getline' and only return the first letter.
char GetCH ()
{
string c;
getline(cin, c);
return c[0];
};
It works like a charm now. Thanks to everyone for the help.