junk left in cin buffer - c++

i need to prevent the junk left in the buffer as entering a value for a switch case menu from being used in a function called by the menu where their is user input.
menu code
void menu()
{
bool done = false;
string input;
while(!done)
{
cout << "Welcome to the DVD database." << endl;
cout << "1. Add A DVD" << endl;
cout << "2. Delete a DVD." << endl;
cout << "3. Edit a DVD." << endl;
cout << "4. List By Category." << endl;
cout << "5. Retrieve by a DVD by Title." << endl;
cout << "6. Display collection by year" << endl;
cout << "7. Display collection by title" << endl;
cout << "-999. Exit program" << endl;
cout << "Please choose an option by entering the corresponding number" << endl;
cin >> input;
int value = atoi(input.c_str());
switch(value)
{
case 1:addDVD(); break;
case 2:deleteDVD(); break;
// case 3:editDVD(); break;
case 4:listByCategory();break;
case 6:displayByYear();break;
case 7:displayByTitle();break;
case -999: writeToFile(); exit(0); break;
default : cout <<"Invalid entry"<< endl; break;
}
}
}
void retrieveByTitle()
{
string search;
int size = database.size();
int index = 0;
bool found = false;
cin.ignore();
cout << "Please enter the title of the DVD you would like to retrieve: " << endl;
getline(cin,search);
cout << search;
while(!found && index<size)
{
if(database.at(index)->getTitle().compare(search)==0)
{
cout << database.at(index)->toString();
break;
}
}
cout << endl;
}
if 5 is entered in the menu, the program skips the user input in the method

This code works, but it has the same problem you describe if you eliminate the 'cin.ignore()', which removes the extra delimiters ignored by the cin >> operator:
#include <iostream>
#include <climits>
using namespace std;
int main() {
string a, b;
while (true) {
cout << "write 'x' to exit: " << endl;
cin >> a;
if (a == "x") {
break;
}
cout << "read '" << a << "'" << endl;
cout << "now write a line: " << endl;
cin.clear(); // clears cin status
cin.ignore(INT_MAX); // clears existing, unprocessed input
getline(cin, a);
cout << "read '" << a << "'" << endl;
}
return 0;
}

When dealing with interactive user input you should use std::getline()
The std::cin is flushed to the application every time you hit <enter>. So this is the logical junks you should read data from the user in.
std::string answer;
std::cout << "Question:\n";
std::getline(std::cin, answer);
This gets you everything the user provided in response to the previous question.
Once you have the input you should get the value you think is on the input. Once you have this you should check if there is any other junk on the input (if there is then abort and re-try) otherwise validate the data you expected.
If you were expected an integer;
std::stringstream linestream(answer);
int value;
std::string junk;
if ((answer >> value)) && (!(answer >> junk)))
{
// If you got data
// and there was no junk on the line you are now good to go
}
In your specific example there is already a simple way to do this:
std::getline(std::cin, input);
int value = boost::lexical_cast<int>(input); // throws an exception if there is not
// an int on the input (with no junk)

Related

Trying to incorporate void functions into switch statements

I'm a novice coding student and trying to create a menu using structs, functions, and switch statements to make a mini database for a class assignment. I'm trying to implant the functions into the switch statements.
I'm getting errors on lines 87 and 137 and I'm not sure where I'm going wrong. Any help, explanation, or correction is much appreciated.
#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>
using namespace std;
// Jaret Clark
// Week 3 Interactive Assignment
// INT 499
// Prof. Joseph Issa
// 03/31/2022
struct EZTechMovie {
string name;
string *cast[10];
string rating;
};
void displaymovie(EZTechMovie movie, int cast_num) {
int i;
cout << endl;
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
cout << endl;
cout << "Your entry:\n";
//Movies
cout << endl;
cout << "Movie TITLE: " << movie.name;
cout << endl;
//Movie rating
cout << "Movie Rating: " << movie.rating;
cout << endl;
//Cast name
cout << "Main Cast Members: \n";
//loop for cast members ~ stores in array
for (int i = 0; i < cast_num; ++i) {
cout << movie.cast[i];
cout << endl;
}
}
void mainmenu() {
string movie_input;
int m;
cout << endl;
cout << "Would you like to store movies into database? (yes or no) ";
getline(cin, movie_input);
cout << endl;
if (movie_input == "yes") {
string cont;
string cast_name;
int x, m, n, i, cast_num;
EZTechMovie moviedb[100];
cout << endl;
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
cout << endl;
for (n = 0; n < 100; n++) {
cout << "Movie Title: ";
getline(cin, moviedb[n].name);
cout << endl;
cout << "Movie rating: ";
getline(cin, moviedb[n].rating);
cout << endl;
cout << "How many cast do you want to enter? ";
cin >> cast_num;
cout << endl;
cin.ignore();
for (i = 0; i < cast_num; i++) {
cout << "Cast name: First and Last name: ";
getline(cin, moviedb[n].cast[i]);
cout << endl;
}
cout << endl;
displaymovie(moviedb[n], cast_num);
cout << endl;
cout << "Add more movies? (yes or no) ";
getline(cin, cont);
if (cont == "no") {
break;
}
cout << endl;
cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" << endl;
cout << endl;
}
}
else if (movie_input == "no") {
return;
}
else {
cout << "INVALID Input";
mainmenu();
}
}
// menu
void movieMenu() {
int choice;
EZTechMovie movie;
do {
cout << "***********************Welcome to EZTechMovie Movie Entry Menu***********************" << endl;
cout << "Press 1 to Enter Movie Info - Name, Cast Members, and Rating.\n";
cout << "Press 2 to Retrieve movie info recently entered.\n";
cout << "Press 3 To Quit program.\n";
// evaluate menu options in switch case
switch (choice) {
case 1:
mainmenu();
break;
case 2:
displaymovie(EZTechMovie movie, int cast_num);
break;
case 3:
cout << "Thank you and Goodbye!";
break;
default:
cout: "Invalid Selection. Try again!\n";
}
//get menu selection
cin >> choice;
} while (choice != 3);
}
int main() {
movieMenu();
}
Regarding the error on line 87 (getline(cin, moviedb[n].cast[i]);) :
moviedb[n].cast[i] is a std::string*, not std::string like you might have meant.
A quick compilation fix would be to use:
getline(cin, *(moviedb[n].cast[i]));
i.e. dereference the pointer.
However - this code raises other design/programming issues:
Why do you use std::string* and not std::string in the first place.
Why do you use C style array instead of std::vector (or std::array if you can commit to the size). This is relevant for both: string *cast[10]; and EZTechMovie moviedb[100];

C++ Pulling information and looping to display

Not under standing looping for arrays. Looping through all of grab some or search. Can someone explain the process? Thanks in advance. Sorry if duplicate. I looked around and couldnt find a solid explaination that I could understand.
#include <fstream>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
void allContacts(string names[], string phones[])
{
cout << "Showing all contacts... Press Q to go back to main menu" << endl;
}
void addName(string names[], string phones[])
{
bool keepGoing;
string input;
beginning:
for (int i = 0; i < sizeof(names); i++)
{
cout << "Enter contact name: ";
cin >> names[i];
cout << "Enter contact number: ";
cin >> phones[i];
cout << "Do you have another contact to add? y or no" << endl;
cin >> input;
if(input == "y" || input == "Y")
{
goto beginning;
}
if(input == "n" || input == "N")
{
cout << "Contacts you have entered: " << endl;
cout << names[i] << " : " << phones[i] << endl;
}
}
}
void searchName(string names[], string phones[])
{
string name;
cout << "Enter Name: ";
cin >> name;
cout << "Search for a name or Press Q to go back to main menu" << endl;
for (int i = 0; i < sizeof(names); i++){
if (name == names[i])
{
cout << counter << names[i] << " 's phone number is: " << phones[i] << endl;
} else {
cout << "No results found";
}
}
}
int main()
{
string names[100];
string phones[100];
int choice;
cout << "============================" << endl;
cout << "=== Welcome to PhoneBook ===" << endl;
cout << "============================" << endl;
cout << "1- Add a New Contact" << endl;
cout << "2- Search By Name" << endl;
cout << "3- Display All" << endl;
cout << "0- Exit" << endl;
cout << "Select a number: " << endl;
cin >> choice;
switch(choice)
{
case 1:
addName(names, phones);
break;
case 2:
searchName(names, phones);
break;
case 3:
allContacts(names, phones);
break;
case 0:
cout << "Exiting PhoneBook...";
break;
}
}
In C++ arrays lose attributes when passed to functions. Those attributes are capacity and size (number of filled slots). You will need to pass this additional information for each array:
void addName(string names[], unsigned int names_capacity, unsigned int names_size,
string phones[], unsigned int phones_capacity, unsigned int phones_size)
To get around this, you can use std::vector. The std::vector knows its capacity and size, so you don't have to pass additional attributes to your function.
Also, if you use tolower or toupper before you compare, you only need to make one comparison:
char input;
cout << "Do you have another contact to add? y or n" << endl;
cin >> input;
input = toupper(input);
if(input == 'Y')
When using strings, you can convert them to all uppercase or all lowercase by using std::transform, such as:
std::transform(input.begin(),
input.begin(), input.end(),
tolower);

How to rename a file in C++

The part of code where I rename the file just won't work. I tried writing it separately in another project, it works. Help me please.
#include <iostream>
#include <stdio.h>
#include <fstream>
using namespace std;
int main () {
char address[] = "";
char newname[] = "";
int action;
char confirm;
int result;
cout << "File Manipulator 1.0" << endl;
cout << "--------------------" << endl << endl;
cout << "Type the full address of a file you wish to manipulate." << endl << endl;
ADDRESS:cin >> address;
fstream file(address);
if (!file.good()) {
cout << "The selected file does not exist! Try again. ";
goto ADDRESS;
} else {
cout << endl << "-----------------------------------" << endl;
cout << "Type 1 to move the selected file." << endl;
cout << "Type 2 to rename the selected file." << endl;
cout << "Type 3 to delete the selected file." << endl;
cout << "-----------------------------------" << endl << endl;
ACTION:cin >> action;
if (action == 1) {
cout << 1;
} else if (action == 2) {
cout << "Enter the new name: ";
cin >> newname;
cout << "Are you sure you want to rename the selected file? Y/N ";
CONFIRM:cin >> confirm;
if (confirm == 'Y' || 'y') {
result = rename(address, newname);
if (result == 0) {
cout << "renamed";
} else {
perror("not renamed");
}
} else if (confirm == 'N' || 'n') {
cout << "No";
} else {
cout << "You typed an invalid command! Try again. ";
goto CONFIRM;
}
} else if (action == 3) {
cout << 3;
} else {
cout << "You typed an invalid command! Try again." << endl;
goto ACTION;
}
}
return 0;
}
BTW the whole code is not finished, so check just the renaming part. Thanks.
Well, this is the solution.
#include <iostream>
#include <cstdio>
#include <fstream>
#include <string>
using namespace std;
int main() {
string address;
string newname;
Here you can see I used strings instead of char arrays.
char input;
int action;
char confirm;
int result;
cout << "File Manipulator 1.0" << endl;
cout << "--------------------" << endl << endl;
cout << "Type the full address of a file you wish to manipulate." << endl << endl;
getline(cin, address);
ifstream myfile(address.c_str());
I used ifstream with c_str() function which passes contents of a std::string into a C style string.
// try to open the file
if (myfile.is_open())
{
When the condition is met, you must close the opened file in order to be able to manipulate/work with it later.
myfile.close();
CREATE:cout << endl << "-----------------------------------" << endl;
cout << "Type 1 to move the selected file." << endl;
cout << "Type 2 to rename the selected file." << endl;
cout << "Type 3 to delete the selected file." << endl;
cout << "-----------------------------------" << endl << endl;
cin >> action;
switch (action)
{
case 1:
{
// do nothing.
}
break;
case 2:
{
// rename file.
cout << "Enter the new name" << endl << endl;
cin.ignore();
I used here the ignore() function to ignores the amount of characters I specify when I call it.
getline(cin, newname);
cout << "Are you sure you want ot rename the selected file ? Y/N" << endl << endl;
cin >> confirm;
if (confirm == 'Y' || confirm == 'y')
{
Same case with c_str() that i explained earlier.
rename(address.c_str(), newname.c_str());
}
}
break;
case 3:
{
// delete file.
remove(address.c_str());
}
break;
default:
{
cout << "You typed an invalid command!" << endl;
}
break;
}
}
else
{
cout << "The selected file does not exist! Would you like to create it? ";
cin >> input;
If the file name you input doesn't exist, you are prompted to create a file with the specified name, then you are redirected with goto to the manipulation menu.
if (input == 'y' || input == 'Y')
{
// create the file.
ofstream output(address.c_str());
output.close();
cout << "File created";
goto CREATE;
}
}
return 0;
}
Thanks for trying anyway :)

How can I avoid bad input from a user?

I am a very newbie programmer, so I don't really know much about writing code to protect the application.. Basically, I created a basicMath.h file and created a do while loop to make a very basic console calculator (only two floats are passed through the functions). I use a series of if and else if statements to determine what the users wants to do. (1.add, 2.subtract, 3.multiply, 4.divide) I used a else { cout << "invalid input" << endl;} to protect against any other values, but then I tried to actually write a letter, and the program entered a infinite loop. Is there anyway to protect against users who accidentally hit a character instead of a number?
`#include <iostream>
#include "basicMath.h"
using namespace std;
char tryAgain = 'y';
float numOne = 0, numTwo = 0;
int options = 0;
int main()
{
cout << "welcome to my calculator program." << endl;
cout << "This will be a basic calculator." << endl;
do{
cout << "What would you like to do?" << endl;
cout << "1. Addition." << endl;
cout << "2. Subtraction." << endl;
cout << "3. Multiplication" << endl;
cout << "4. Division." << endl;
cin >> options;
if (options == 1){
cout << "Enter your first number." << endl;
cin >> numOne;
cout << "Enter your second number." << endl;
cin >> numTwo;
cout << numOne << " + " << numTwo << " = " << add(numOne, numTwo) << endl;
}
else if (options == 2){
cout << "Enter your first number." << endl;
cin >> numOne;
cout << "Enter your second number." << endl;
cin >> numTwo;
cout << numOne << " - " << numTwo << " = " << subtract(numOne, numTwo) << endl;
}
else if (options == 3){
cout << "Enter your first number." << endl;
cin >> numOne;
cout << "Enter your second number." << endl;
cin >> numTwo;
cout << numOne << " * " << numTwo << " = " << multiply(numOne, numTwo) << endl;
}
else if (options == 4){
cout << "Enter your first number." << endl;
cin >> numOne;
cout << "Enter your second number." << endl;
cin >> numTwo;
cout << numOne << " / " << numTwo << " = " << divide(numOne, numTwo) << endl;
}
else {
cout << "Error, invalid option input." << endl;
}
cout << "Would you like to use this calculator again? (y/n)" << endl;
cin >> tryAgain;
}while (tryAgain == 'y');
cout << "Thank you for using my basic calculator!" << endl;
return 0;
}
`
One way would be to use exception handling, but as a newbie you're probably far from learning that.
Instead use the cin.fail() which returns 1 after a bad or unexpected input. Note that you need to clear the "bad" status using cin.clear().
A simple way would be to implement a function:
int GetNumber ()
{
int n;
cin >> n;
while (cin.fail())
{
cin.clear();
cin.ignore();
cout << "Not a valid number. Please reenter: ";
cin >> n;
}
return n;
}
Now in your main function wherever you are taking input, just call GetNumber and store the returned value in your variable. For example, instead of cin >> numOne;, do numOne = GetNumber();
When you input to cin, it is expecting a specific type, such as an integer. If it receives something that it does not expect, such as a letter, it sets a bad flag.
You can usually catch that by looking for fail, and if you find it, flush your input as well as the bad bit (using clear), and try again.
Read a whole line of text first, then convert the line of text to a number and handle any errors in the string-to-number conversion.
Reading a whole line of text from std::cin is done with the std::getline function (not to be confused with the stream's member function):
std::string line;
std::getline(std::cin, line);
if (!std::cin) {
// some catastrophic failure
}
String-to-number conversion is done with std::istringstream (pre-C++11) or with std::stoi (C++11). Here is the pre-C++11 version:
std::istringstream is(line);
int number = 0;
is >> number;
if (!is) {
// line is not a number, e.g. "abc" or "abc123", or the number is too big
// to fit in an int, e.g. "11111111111111111111111111111111111"
} else if (!is.eof()) {
// line is a number, but ends with a non-number, e.g. "123abc",
// whether that's an error depends on your requirements
} else {
// number is OK
}
And here the C++11 version:
try {
std::cout << std::stoi(line) << "\n";
} catch (std::exception const &exc) {
// line is not a number, e.g. "abc" or "abc123", or the number is too big
// to fit in an int, e.g. "11111111111111111111111111111111111"
std::cout << exc.what() << "\n";
}

C++ Address Book Array and Textfile

Sorry for the lack of previous explanation to my school's assignment. Here's what I'm working with and what I have / think I have to do.
I have the basic structure for populating the address book inside an array, however, the logic behind populating a text file is a bit beyond my knowledge. I've researched a few examples, however, the implementation is a bit tricky due to my novice programming ability.
I've gone through some code that looks relevant in regard to my requirements:
ifstream input("addressbook.txt");
ofstream out("addressbook.txt");
For ifstream, I believe implementing this into the voidAddBook::AddEntry() would work, though I've tried it and the code failed to compile, for multiple reasons.
For ostream, I'm lost and unsure as to how I can implement this correctly. I understand basic file input and output into a text file, however, this method is a bit more advanced and hence why I'm resorting to stackoverflow's guidance.
#include <iostream>
#include <string.h> //Required to use string compare
using namespace std;
class AddBook{
public:
AddBook()
{
count=0;
}
void AddEntry();
void DispAll();
void DispEntry(int i); // Displays one entry
void SearchLast();
int Menu();
struct EntryStruct
{
char FirstName[15];
char LastName[15];
char Birthday[15];
char PhoneNum[15];
char Email[15];
};
EntryStruct entries[100];
int count;
};
void AddBook::AddEntry()
{
cout << "Enter First Name: ";
cin >> entries[count].FirstName;
cout << "Enter Last Name: ";
cin >> entries[count].LastName;
cout << "Enter Date of Birth: ";
cin >> entries[count].Birthday;
cout << "Enter Phone Number: ";
cin >> entries[count].PhoneNum;
cout << "Enter Email: ";
cin >> entries[count].Email;
++count;
}
void AddBook::DispEntry(int i)
{
cout << "First name : " << entries[i].FirstName << endl;
cout << "Last name : " << entries[i].LastName << endl;
cout << "Date of birth : " << entries[i].Birthday << endl;
cout << "Phone number : " << entries[i].PhoneNum << endl;
cout << "Email: " << entries[i].Email << endl;
}
void AddBook::DispAll()
{
cout << "Number of entries : " << count << endl;
for(int i = 0;i < count;++i)
DispEntry(i);
}
void AddBook::SearchLast()
{
char lastname[32];
cout << "Enter last name : ";
cin >> lastname;
for(int i = 0;i < count;++i)
{
if(strcmp(lastname, entries[i].LastName) == 0)
{
cout << "Found ";
DispEntry(i);
cout << endl;
}
}
}
AddBook AddressBook;
int Menu()
{
int num;
bool BoolQuit = false;
while(BoolQuit == false)
{
cout << "Address Book Menu" << endl;
cout << "(1) Add A New Contact" << endl;
cout << "(2) Search By Last Name" << endl;
cout << "(3) Show Complete List" << endl;
cout << "(4) Exit And Save" << endl;
cout << endl;
cout << "Please enter your selection (1-4) and press enter: ";
cin >> num;
cout << endl;
if (num == 1)
AddressBook.AddEntry();
else if (num == 2)
AddressBook.SearchLast();
else if (num == 3)
AddressBook.DispAll();
else if (num == 4)
BoolQuit = true;
else
cout << "Please enter a number (1-4) and press enter: " << endl;
cout << endl;
}
return 0;
}
int main (){
Menu();
return 0;
}
As it currently stands, I'm still stuck. Here's where I believe I should start:
cout << "Please enter your selection (1-4) and press enter: ";
cin >> num;
cout << endl;
if (num == 1)
AddressBook.AddEntry();
else if (num == 2)
AddressBook.SearchLast();
else if (num == 3)
AddressBook.DispAll();
else if (num == 4)
BoolQuit = true;
//save first name
//save last name
//save dob
//save phone number
//save email
//exit
else
cout << "Please enter a number (1-4) and press enter: " << endl;
cout << endl;
}
Somehow, during menu option 4 the array should dump the data into a .txt file and arrange it in a way that it can be easily imported upon reloading the program. I'm a little confused as to how I can store the array data from each character array into a .txt file.
Well first, if the input is coming from the file input, then instead of doing cin >> x you would have to do input >> x. If it's coming from standard input (the keyboard), then you can use cin.
Also, your else if statement should be something like this:
while (true)
{
// ...
else if (num == 4)
{
for (int i = 0; i < AddressBook.count; ++i)
{
AddBook::EntryStruct data = AddressBook.entries[i];
out << data.FirstName << " " << data.LastName
<< std::endl
<< data.Birthday << std::endl
<< data.PhoneNum << std::endl
<< data.Email;
}
}
break;
}