Conditionally Breaking A Long Sequence Of Inputs? - c++

I have a personal project I've been working on. To work, it needs to accept a lot of data (relatively) from the user, in the form of four different kinds of data for 12 users. As such, I have quite a lengthy sequence of statements similar to this:
cout << '\n' << "Monster A's name is: ";
cin >> nameA;
cout << '\n' << "Monster A rolled: ";
cin >> rollM_A;
cout << '\n' << "Monster A's Dex is: ";
cin >> DexA;
cout << '\n' << "Monster A's Mod is: ";
cin >> ModA;
cout << '\n' << "Monster A's Level is: ";
cin >> LvlA;
etc.
Occasionally, however, there might only be a need to input data for less than 12 monsters, say, 5-6 or even 1-2. I'd like to be able to use a command to skip the input section to the rest of the code based on something like a keystroke. i.e. If the user has put in data for 5 monsters, and that's all they require, they could hit the backslash to skip the rest of the input sequence.
Does what I'm talking about make any sense/is there an STL command to do what I'm looking for? Currently, this process isn't looped, but would exiting it be easier if it was inside a loop? I did have a thought of trying something like this:
while(cin.get() != '\') {
cout << '\n' << "Monster A's name is: ";
cin >> nameA;
//etc...
}
EDIT: The above attempt builds, but upon entering the desired keystroke, it quickly and endlessly prints the user prompts without accepting data. Any advice? All I want is for it to break and move on. Can I use a switch statement effectively?
Thanks!

That could work. You can also use EOF, which is more general, than '\'. Then when you are done, hit Ctrl-D to send the EOF, and you are done. This takes care of the situation when some player enters '\' as the Monster's name.

I just tested this set of code and it seems to work how you would like. Of course you will have to modify it to fit your original application.
std::string in;
while (true) {
std::cout << "Enter a name\n";
std::cin >> in;
if (in == "#")
break;
std::cout << "\nMonster A's name is: " << in << "\n";
}
In order to incorporate the limit of the number of monsters, rather than having the true parameter passed into the while loop, simply add a counter to how many monsters are created and break on that condition:
int num_monsters = 0;
while (num_monsters <= 12) {
...
num_monsters++;
}
Hope this helps.

You can check if the name is \, it is not user friendly but it works.
string name;
while (name != "\\") {
cout << '\n' << "Monster A's name is: ";
cin >> name;
cout << "Received " << name << '\n';
if (name != "\\") {
// do something with name ...
}
}
If the loop still loops endlessly, refer to How do I flush the cin buffer? and try clearing stdin buffer.
[edit] I fixed an error in the loop

Here's something I wrote.
struct Monster {
string name;
bool roll;
float dex;
float mod;
float level;
Monster(void) :
name(),
roll(false),
dex(0),
mod(0),
level(0) { }
};
bool getMonsterInformationFromStdin(int index, Monster& monster) {
string end_char = "\\";
string name, roll, dex, mod, level;
cout << '\n' << "Monster " << index << "'s name is: ";
cin >> name;
if (name.compare(end_char) == 0) return false;
monster.name = name;
cout << '\n' << "Monster " << index << " rolled: ";
cin >> roll;
if (roll.compare(end_char) == 0) return false;
monster.roll = (roll[0] == 'y' || roll[0] == 'Y') ? true : false;
cout << '\n' << "Monster " << index << "'s Dex is: ";
cin >> dex;
if (dex.compare(end_char) == 0) return false;
monster.dex = atof(dex.c_str());
cout << '\n' << "Monster " << index << "'s Mod is: ";
cin >> mod;
if (mod.compare(end_char) == 0) return false;
monster.mod = atof(mod.c_str());
cout << '\n' << "Monster " << index << "'s Level is: ";
cin >> level;
if (level.compare(end_char) == 0) return false;
monster.level = atof(level.c_str());
return true;
}
int main(int argc, char** argv) {
int max_monsters = 10;
for (int i = 0; i < max_monsters; i++) {
Monster m;
if( !getMonsterInformationFromStdin(i, m) ) break;
string roll = m.roll ? "rolled" : "didn't roll";
cout << m.name << " " << roll << " dex: " << m.dex << " mod: " << m.mod << " level: " << m.level << endl;
}
return 0;
}

I think this is problem can be solved by using a sentinel Monster name, as u can see below.
const string SentinelName = "%^&";
while(true)
{
cout << '\n' << "Monster A's name is(if u want to end, pls input %^&): ";
cin >> nameA;
if(strcmp(nameA, SentinelName.s_str())
break;
cout << '\n' << "Monster A rolled: ";
cin >> rollM_A;
cout << '\n' << "Monster A's Dex is: ";
cin >> DexA;
cout << '\n' << "Monster A's Mod is: ";
cin >> ModA;
cout << '\n' << "Monster A's Level is: ";
cin >> LvlA;
}
Hope this can solve ur problem. Besides, A little advice for ur code, u can encapsulate the attributes of the monster, such as the name, mode level, etc into a class and this will make ur code look fancier.

Related

C++ If/Else loop problems

Checking user input. If the incorrect input is entered, it loops fine. If the correct input is entered, It has 2 be entered again in order for it to register. Help?
int deposit;
cout << "How much do you want to deposit?" << endl;
cin >> deposit;
if (!(cin >> deposit)) { // If the input is not equal to the data type of
// deposit
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "Invalid Input" << endl;
} else {
while (infile >> u >> p >> b) {
if (checkUser == u) {
int newBalance;
cout << "Your amount of $" << deposit << " has been added to your account"
<< endl;
int fileBalance;
stringstream convert(b); // object from the class stringstream
convert >> fileBalance; // the objects has the value of B and
newBalance = fileBalance + deposit;
outfile << checkUser << ' ' << checkPass << ' ' << newBalance << endl;
} else {
outfile << u << ' ' << p << ' ' << b << endl;
}
}
}
You're entering input twice because you go through cin>>deposit at least twice:
cout << "How much do you want to deposit?" << endl;
cin >> deposit;
if (!(cin >> deposit)) {
Just get rid of the first cin:
cout << "How much do you want to deposit?" << endl;
if (!(cin >> deposit)) {
Also, have a look at setting up clang-format with your code editor or IDE. It makes your code look nice without effort and will help you spot errors. There's also an online version available here: http://format.krzaq.cc/
It's because here:
if(!(cin>>deposit))
you're re-reading from standard input. When you first do cin>>deposit, you've already stored the value in the variable, so you just have to check the deposit variable, or whatever you want to do with the inserted data.
Another, cleaner, way of doing it is to just avoid the first cin>>deposit statement.

How to list how many iterations of a struct are stored inside a vector?

I'm new to c++ (and coding in general) and have recently been working with a struct held inside a vector, in this case :
struct Contact{
string name;
string address;
string phone;
string email;};
vector<Contact> contacts;
So, one of my functions involves searching through each of the contacts to find the one for which the string stored in name matches a search input. To do this I made a for loop as such:
for(int i = 0; i < contacts.size(); i++){
if(contacts[i].name == searchInput){
cout << contacts[i].address << "\n\r" << contacts[i].phone << "\n\r" << contacts[i].email;
But for some reason this was only able to find the correct contact if it was the name stored at:
contacts[0].name
and none of the others. So while trying to figure out what was wrong, I decided to do
cout << contacts.size();
which I thought should output 3, because I have only three contacts stored. Yet for some reason, it output 7. Is there anyway for me to accurately list the number of iterations of Contact stored in the contacts vector in order to get my for loop to work?
Edit for my full code:
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;
struct Contact
{
string name;
string address;
string phone;
string email;
};
bool go;
bool a = false;
char command;
string endL = "\n\r";
string tab = "\t";
string line;
int i;
int counter = 0;
int contactCounter = 0;
vector<Contact> contacts;
void add(){
contacts.push_back(Contact());
int newcontact = contacts.size() - 1;
string input;
cout << "Enter the name: " << endL;
cin >> input;
contacts[newcontact].name = input;
cout << "Enter the address: " << endL;
cin >> input;
contacts[newcontact].address = input;
cout << "Enter the phone number: " << endL;
cin >> input;
contacts[newcontact].phone = input;
cout << "Enter the email address: " << endL;
cin >> input;
contacts[newcontact].email = input;
}
void search(string name){
for(int i = 0; i < contacts.size(); i++){
if(contacts[i].name == name){
cout << "Name: " << contacts[i].name << endL << "Address: " << contacts[i].address << endL << "Phone Number: " << contacts[i].phone << endL << "Email: " << contacts[i].email << endL << endL;
a = true;
}
}
if(a == false){
cout << "There is no contact under that name." << endL;
}
}
int main() {
ifstream phonebook;
phonebook.open("phonebook.txt");
if(phonebook.is_open()){
while(getline(phonebook,line)){
if(line.empty() == false){
if(counter % 4 == 0){
contacts.push_back(Contact());
contacts[contactCounter].name = line;
}else if(counter % 4 == 1){
contacts[contactCounter].address = line;
}else if(counter % 4 == 2){
contacts[contactCounter].phone = line;
}else if(counter % 4 == 3){
contacts[contactCounter].email = line;
contactCounter++;
}
counter++;
}
}
}else{cout << "an error has occurred while opening the phonebook";}
phonebook.close();
cout << contacts.size() << endL;
cout << "Enter a command." << endL << tab << "To add a contact, enter '+'" << endL << tab << "To search for a contact, enter 's'" << endL << tab << "To delete a contact, enter '-'" << endL << tab << "To quit the program, enter 'q'" << endL;
cin >> command;
while(command != 'q'){
if(command == '+'){
add();
command = '/';
}
else if(command == 's'){
string searched;
cout << "Please enter who you would like to search for: ";
cin >> searched;
search(searched);
command = '/';
}
else if(command == '-'){
cout << "Not done." << endL;
command = '/';
}
else if(command == '/'){
cout << "Enter a command." << endL << tab << "To add a contact, enter '+'" << endL << tab << "To search for a contact, enter 's'" << endL << tab << "To delete a contact, enter '-'" << endL << tab << "To quit the program, enter 'q'" << endL;
cin >> command;
}
else{
cout << "That command is invalid." << endL;
cout << "Enter a command." << endL << tab << "To add a contact, enter '+'" << endL << tab << "To search for a contact, enter 's'" << endL << tab << "To delete a contact, enter '-'" << endL << tab << "To quit the program, enter 'q'" << endL;
cin >> command;
}
}
ofstream newbook;
newbook.open("phonebook.txt");
if(newbook.is_open()){
for(int i=0; i < contacts.size(); i++){
newbook << contacts[i].name << endl;
newbook << contacts[i].address << endl;
newbook << contacts[i].phone << endl;
newbook << contacts[i].email << endl;
newbook << endL;
}
}else{cout << "there was an issue saving your contacts" << endL;}
newbook.close();
return 0;
}
There's actually nothing wrong with your code except this line
string endL = "\n\r";
Which should really only be
string endL = "\n";
\n is automatically converted to the line endings used by the system, which traditionally is \n (0x0a) on unix systems and \r\n (0x0d0a) on Windows.
But, how did this affect the program so much? Well it only takes affect after the phonebook is written at the end of the program so that phonebook.txt contains these bogus line endings that have \r\n\r at the end (on Windows). So when the file is read, it reads up until the new line \r\n and sees \rPerson Name as line after! Which explains why searching was failing.
You also may see some additional bogus contacts generated because there may be some extra \rs at the end which read as a single line each. Without looking at your phonebook.txt I can't say for certain why you have an additional 4 though I'd guess extra \rs would be the cause.
All in all, use \n for new lines.
To answer the title, vector::size() is THE way to get the number of stored objects in a vector. It's not lying to you.
Using the range based for loop ensures that you won't hit any nonexistent contacts:
for(auto&& contact: contacts)
{
// Contact contact is now accessible.
}
Also, it is probably not a good idea to store a as a global variable. What happens if you execute search twice?

c++ While loops prints the couts twice

In my code, the while loop prints the cout twice when it should print it once, as well as the function's couts. I don't understand why it's doing this - it is supposed to display
What would you like to do?
Deposit
Withdraw
Cancel
But, it displays that twice.
while (yesNo == 'Y') {
cout << "What would you like to do?"
<< endl
<< endl;
menu();
getline(cin, bankChoice);
if (bankChoice == "Withdraw")
{
withdrawTotal = withdraw(bankAmount);
bankAmount = withdrawTotal;
cout << "You now have $"
<< bankAmount
<< " in your account."
<< endl;
cout << "Would you like to do anything else?"
<< endl
<< "Y/N: ";
cin >> yesNo;
}
if (bankChoice == "Deposit")
{
depositTotal = deposit(bankAmount);
bankAmount = depositTotal;
cout << "You now have $"
<< bankAmount
<< " in your account."
<< endl;
cout << "Would you like to do anything else?"
<< endl
<< "Y/N: ";
cin >> yesNo;
}
if (bankChoice == "Cancel") {
return 0;
}
}
That is the loop I am using. If additional code is needed I can post it as well, but this is the part that is causing the issue. I've tried the code without it and it works fine, but I'd like to get the code to loop until the user enters 'N'.
You are using both std::getline and operator>> to read from std::cin. operator>> does not consume the trailing newline, so the next call to std::getline() will immediately read the following newline and interpret it as an empty line of text that was entered. This will run through the loop, and go back up to the top, for the second prompt.
Never use operator>> with std::cin when you intend to read a single line of text.
The following short example demonstrates this point:
#include <iostream>
#include <string>
int main()
{
char c;
std::string l;
std::cin >> c;
std::cout << "A line of text please: ";
std::getline(std::cin, l);
}
Run it, enter "Y", and try to figure it out, yourself, why the program terminates immediately.
Once again: don't use operator>> to read lines of text from std::cin. It is a recipe for grief, and bugs.
In addition to Sam's answer, I'd like to recommend that you extract common functionality outside your two if statements:
std::string yesNo;
while (yesNo.compare("Y") == 0) {
cout << "What would you like to do?"
<< endl
<< endl;
menu();
getline(cin, bankChoice);
if (bankChoice == "Cancel")
return 0;
if (bankChoice == "Withdraw") {
withdrawTotal = withdraw(bankAmount);
bankAmount = withdrawTotal;
}
if (bankChoice == "Deposit") {
depositTotal = deposit(bankAmount);
bankAmount = depositTotal;
}
cout << "You now have $"
<< bankAmount
<< " in your account."
<< endl;
cout << "Would you like to do anything else?"
<< endl
<< "Y/N: ";
std::getline(std::cin, yesNo);
}

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;
}