Problem with logic when usng if statements - c++

I am writing a small program to accept a name and a score. The program will store data in vectors. Then the program will print the name and score of each entry. Then the program should be able to search for a stored name, and if found display the name and corresponding score, if not found it should display a "not found" message. The program works great, up until the point where a name is entered to be searched, if the name is stored, it will print the name and score correctly. If the name is not found, it will display "not found" correctly as well. The problem is if the name is found, the program will also display the "not found" message as well as the name and score. I am not sure where my logic is flawed.
I have tried using else if, I have tried separate loops, I have tried putting the if statement in the same loop. I have tried using a break statement. Nothing I try will remove the "not found" message when a record is found. Since this is just a program to help me learn, it isn't that big of a deal, however I would like to understand what is going wrong to help me learn more, and to fix this annoying issue.
#include<iostream>
#include<vector>
#include<string>
using namespace std;
inline void keep_window_open() { char ch; cin >> ch; }
vector<string> listOfNames{};
vector<int> listOfScores{};
string name = " ";
int score = 0;
int main()
{
cout << "Please enter a name followed by a score: \n";
cout << "Press enter when finished\n";
cout << "To quit enter NoName 0\n";
while (cin >> name >> score && name != "NoName")
{
for (int i = 0; i < listOfNames.size(); ++i)
{
if (name == listOfNames[i])
{
cout << "error name is a duplicate: \n";
}
}
listOfNames.push_back(name);
listOfScores.push_back(score);
}
cout << "\nlist of names and scores entered\n";
for (int i = 0; i < listOfNames.size(); ++i)
{
cout << listOfNames[i] << ',' << listOfScores[i] << "\n";
}
cout <<"Please enter a name to search for\n";
string searchName = " ";
cin >> searchName;
for (int i = 0; i < listOfNames.size(); ++i)
{
if (searchName == listOfNames[i])
{
cout << listOfNames[i] << ',' << listOfScores[i]<< "\n";
break;
}
}
for (int i = 0; i < listOfNames.size(); ++i)
{
if (searchName != listOfNames[i])
{
cout << "Not found\n";
break;
}
}
keep_window_open();
return 0;
}
If the user enters John 22 and Tim 28, it will print the list, then when a user searched for a name, if the name is not found, it will print "Not found", however if the name is found, it will print the name and score, as it should, but the next line it prints is "Not found". This line should only print if the record is not found.
After doing some more testing I found that if I only enter one value, one name and one score, then do a search, it will not print the "Not found" message after the name and score, it only happens when there are multiple entries. Which confuses me even more. I am not sure why it would work correctly with only one value, and not multiple values.

You made a minor semantic/logical error.
After you have your list with names and scores, you compare all values in the list with the search string. And if you have found it, then you display it. That works fine. No problem.
But in your second loop you have a logical problem. In your list are tons of names that are not equal. And only one is equal. So if you run through you loop, you will ALWAYS find names, that are not equal. And then you will see the output that you do not want to have.
So, your loop is asking: Is any entry in the list "not equal" to the search string. And yes, always any one is.
You need to modify your logic to: Are all values in the list not equal to the search string.
But logically, this is also not necessary. You do not need to run the full loop again. If you have checked for somebody in the list and you found somebody, the he is NOT NOT in the list. And vice versa. So, he either is in the list or not.
You should add a boolean variable to check the state and then add an if/else statement.
I modified a part of code for you:
cout << "Please enter a name to search for\n";
string searchName = " ";
cin >> searchName;
// In this variable, we will store the result of our search, this can be true or false
bool weFoundSomebody = false;
// If we find somebody, then we will remember the index, where we found him.
int indexForFoundGuy = 0;
for (int i = 0; i < listOfNames.size(); ++i)
{
if (searchName == listOfNames[i])
{
weFoundSomebody = true;
indexForFoundGuy = i;
break;
}
}
// Could we find somebody or not. Only one is possible
if (weFoundSomebody)
{
cout << listOfNames[indexForFoundGuy] << ',' << listOfScores[indexForFoundGuy] << "\n";
}
else
{
cout << "Not found\n";
}
keep_window_open();

Related

C++ program stuck in an infinite loop

Please note that I am a complete beginner at C++. I'm trying to write a simple program for an ATM and I have to account for all errors. User may use only integers for input so I need to check if input value is indeed an integer, and my program (this one is shortened) works for the most part.
The problem arises when I try to input a string value instead of an integer while choosing an operation. It works with invalid value integers, but with strings it creates an infinite loop until it eventually stops (unless I add system("cls"), then it doesn't even stop), when it should output the same result as it does for invalid integers:
Invalid choice of operation.
Please select an operation:
1 - Balance inquiry
7 - Return card
Enter your choice and press return:
Here is my code:
#include <iostream>
#include <string>
using namespace std;
bool isNumber(string s) //function to determine if input value is int
{
for (int i = 0; i < s.length(); i++)
if (isdigit(s[i]) == false)
return false;
return true;
}
int ReturnCard() //function to determine whether to continue running or end program
{
string rtrn;
cout << "\nDo you wish to continue? \n1 - Yes \n2 - No, return card" << endl;
cin >> rtrn;
if (rtrn == "1" and isNumber(rtrn)) { return false; }
else if (rtrn == "2" and isNumber(rtrn)) { return true; }
else {cout << "Invalid choice." << endl; ReturnCard(); };
return 0;
}
int menu() //function for operation choice and execution
{
int choice;
do
{
cout << "\nPlease select an operation:\n" << endl
<< " 1 - Balance inquiry\n"
<< " 7 - Return card\n"
<< "\nEnter your choice and press return: ";
int balance = 512;
cin >> choice;
if (choice == 1 and isNumber(to_string(choice))) { cout << "Your balance is $" << balance; "\n\n"; }
else if (choice == 7 and isNumber(to_string(choice))) { cout << "Please wait...\nHave a good day." << endl; return 0; }
else { cout << "Invalid choice of operation."; menu(); }
} while (ReturnCard()==false);
cout << "Please wait...\nHave a good day." << endl;
return 0;
}
int main()
{
string choice;
cout << "Insert debit card to get started." << endl;
menu();
return 0;
}
I've tried every possible solution I know, but nothing seems to work.
***There is a different bug, which is that when I get to the "Do you wish to continue?" part and input any invalid value and follow it up with 2 (which is supposed to end the program) after it asks again, it outputs the result for 1 (continue running - menu etc.). I have already emailed my teacher about this and this is not my main question, but I would appreciate any help.
Thank you!
There are a few things mixed up in your code. Always try to compile your code with maximum warnings turned on, e.g., for GCC add at least the -Wall flag.
Then your compiler would warn you of some of the mistakes you made.
First, it seems like you are confusing string choice and int choice. Two different variables in different scopes. The string one is unused and completely redundant. You can delete it and nothing will change.
In menu, you say cin >> choice;, where choice is of type int. The stream operator >> works like this: It will try to read as many characters as it can, such that the characters match the requested type. So this will only read ints.
Then you convert your valid int into a string and call isNumber() - which will alway return true.
So if you wish to read any line of text and handle it, you can use getline():
string inp;
std::getline(std::cin, inp);
if (!isNumber(inp)) {
std::cout << "ERROR\n";
return 1;
}
int choice = std::stoi(inp); // May throw an exception if invalid range
See stoi
Your isNumber() implementation could look like this:
#include <algorithm>
bool is_number(const string &inp) {
return std::all_of(inp.cbegin(), inp.cend(),
[](unsigned char c){ return std::isdigit(c); });
}
If you are into that functional style, like I am ;)
EDIT:
Btw., another bug which the compiler warns about: cout << "Your balance is $" << balance; "\n\n"; - the newlines are separated by ;, so it's a new statement and this does nothing. You probably wanted the << operator instead.
Recursive call bug:
In { cout << "Invalid choice of operation."; menu(); } and same for ReturnCard(), the function calls itself (recursion).
This is not at all what you want! This will start the function over, but once that call has ended, you continue where that call happened.
What you want in menu() is to start the loop over. You can do that with the continue keyword.
You want the same for ReturnCard(). But you need a loop there.
And now, that I read that code, you don't even need to convert the input to an integer. All you do is compare it. So you can simply do:
string inp;
std::getline(std::cin, inp);
if (inp == "1" || inp == "2") {
// good
} else {
// Invalid
}
Unless that is part of your task.
It is always good to save console input in a string variable instead of another
type, e.g. int or double. This avoids trouble with input errors, e.g. if
characters instead of numbers are given by the program user. Afterwards the
string variable could by analyzed for further actions.
Therefore I changed the type of choice from int to string and adopted the
downstream code to it.
Please try the following program and consider my adaptations which are
written as comments starting with tag //CKE:. Thanks.
#include <iostream>
#include <string>
using namespace std;
bool isNumber(const string& s) //function to determine if input value is int
{
for (size_t i = 0; i < s.length(); i++) //CKE: keep same variable type, e.g. unsigned
if (isdigit(s[i]) == false)
return false;
return true;
}
bool ReturnCard() //function to determine whether to continue running or end program
{
string rtrn;
cout << "\nDo you wish to continue? \n1 - Yes \n2 - No, return card" << endl;
cin >> rtrn;
if (rtrn == "1" and isNumber(rtrn)) { return false; }
if (rtrn == "2" and isNumber(rtrn)) { return true; } //CKE: remove redundant else
cout << "Invalid choice." << endl; ReturnCard(); //CKE: remove redundant else + semicolon
return false;
}
int menu() //function for operation choice and execution
{
string choice; //CKE: change variable type here from int to string
do
{
cout << "\nPlease select an operation:\n" << endl
<< " 1 - Balance inquiry\n"
<< " 7 - Return card\n"
<< "\nEnter your choice and press return: ";
int balance = 512;
cin >> choice;
if (choice == "1" and isNumber(choice)) { cout << "Your balance is $" << balance << "\n\n"; } //CKE: semicolon replaced by output stream operator
else if (choice == "7" and isNumber(choice)) { cout << "Please wait...\nHave a good day." << endl; return 0; }
else { cout << "Invalid choice of operation."; } //CKE: remove recursion here as it isn't required
} while (!ReturnCard()); //CKE: negate result of ReturnCard function
cout << "Please wait...\nHave a good day." << endl;
return 0;
}
int main()
{
string choice;
cout << "Insert debit card to get started." << endl;
menu();
return 0;
}

How to check if a string matches an array element?

I'm fairly new to programming, and I'm wondering if there's any way I can compare a string input to an array element? I tried the code below, and I know it's wrong, I just don't know how else to go about this.
#include <iostream>
using namespace std;
int main()
{
string Username[10] = {"name1", "name2", "name3", "name4", "name5", "name6", "name7", "name8", "name9", "name10"};
string login;
int i;
cout << "Enter username login: ";
getline(cin, login);
cout << "\n";
for (i = 0; i < 10; i++)
if (login == Username[i])
{
cout << "Loading user settings...";
}
else
cout << "Error: Wrong username entered. ";
return 0;
}
You can avoid the loop and use the std::find algorithm function.
#include <algorithm>
//...
bool exists = (std::find(std::begin(Username), std::end(Username), login)
!= std::end(Username));
I imagine what you want to see is "Loading user settings..." if there is a match, and "Error: Wrong username entered. " if there is no match. Your if-statement should look like this:
if (login == Username[i]){
cout << "Loading user settings...";
break;
}
and your else-statement should be an else-if in the form of:
else if(i==9) cout << "Error: Wrong username entered. ";
Two things:
1) break functions in a way such that, when the program sees a break, it ends the loop that it is currently using. When you find a match, you don't have to look any farther in the array, so break out of it.
2) You only want to print error if you have looked through the entire array, and that will only happen after you have checked the last element which, in this case, is at index 9. Changing the else to an else-if lets you specify this condition.
You should use an algorithm, like this:
if (std::find(Username, Username + 10, login) != (Username + 10))
cout << "Loading user settings...";
else
cout << "Error: Wrong username entered. ";
You can use a bool flag to check if the user exists or not:
Running sample
//...
bool exists = false;
//...
for (i = 0; i < 10; i++)
if (login == Username[i])
{
exists = true;
break;
}
exists ? std::cout << "Loading user settings..." : std::cout << "Error: Wrong username entered. ";
//...
On a side note, see Why is "using namespace std;" considered bad practice?

Need help to stop program terminating without users consent

The following code is supposed to do as follows:
create list specified by the user
ask user to input number
3.a) if number is on the list , display number * 2, go back to step 2
3.b) if number isn't on the list, terminate program
HOWEVER step 3.a) will also terminate the program, which is defeating the purpose of the while loop.
here is the code :
#include <iostream>
#include <array>
using namespace std;
int main()
{
cout << "First we will make a list" << endl;
array <int, 5>list;
int x, number;
bool isinlist = true;
cout << "Enter list of 5 numbers." << endl;
for (x = 0; x <= 4; x++)
{
cin >> list[x];
}
while (isinlist == true)
{
cout << "now enter a number on the list to double" << endl;
cin >> number;
for (x = 0; x <= 4; x++)
{
if (number == list[x])
{
cout << "The number is in the list. Double " << number << " is " << number * 2 << endl;
}
else
isinlist = false;
}
}
return 0;
}
Please can someone help me to resolve this ?
I would suggest that you encapsulate the functionality of step 3 into a separate function. You could define a function as follows, and then call it at an appropriate location in the main function.
void CheckVector(vector<int> yourlist)
{
.... // Take user input for number to search for
.... // The logic of searching for number.
if (number exists)
{
// cout twice the number
// return CheckVector(yourlist)
}
else
return;
}
The same functionality can be implemented with a goto statement, avoiding the need for a function. However, using goto is considered bad practice and I won't recommend it.
Your issue is that you set isinlist to false as soon as one single value in the list is not equal to the user input.
You should set isinlist to false ay the beginning of your while loop and change it to true if you find a match.
Stepping your code with a debugger should help you understand the issue. I encourage you to try it.

How do I keep track of letters guessed in a Hangman game in C++?

I have been trying to fix this program for the past two days and it is proving to be quite troublesome. It is an assignment for my intro to C++ course and has given me nothing but trouble. I have searched this board, posted on Cplusplus.com and spent hours on Google, looking for some assistance.
Here is my problem. I have been given a program and need to add a few features to it:
I have to save the users entries.
I have to display an error message if the user enters the same entry twice.
Seems simple? Not for a beginner such as myself. Here is the code, with what I have attempted to add to it in order to meet the problem's requirements.
int main()
{
//declare variables
string origWord = "";
string letter = "";
char dashReplaced = 'N';
char gameOver = 'N';
int numIncorrect = 0;
string displayWord = "-----";
string letterGuess[26];
//get original word
do //begin loop
{
cout << "Enter a 5-letter word in uppercase: ";
getline(cin, origWord);
} while (origWord.length() != 5);
//clear the screen
system("cls");
//start guessing
cout << "Guess this word: " <<
displayWord << endl;
while (gameOver == 'N')
{
cout << "Enter an uppercase letter: ";
cin >> letter;
//Entry Storage and Error Message. This is my problem.
for (int x = 0; x < 26; x++)
{
letterGuess[x] = letter;
for (int i = x; i < 26; i++)
{
if (i != x)
{
if (letterGuess[x] == letterGuess[i])
{
cout << "Letter already entered. Choose another letter."
<< endl;
}
}
}
}
//search for the letter in the original word
for (int x = 0; x < 5; x += 1)
{
//if the current character matches
//the letter, replace the corresponding
//dash in the displayWord variable and then
//set the dashReplaced variable to 'Y'
if (origWord.substr(x, 1) == letter)
{
displayWord.replace(x, 1, letter);
dashReplaced = 'Y';
} //end if
} //end for
//if a dash was replaced, check whether the
//displayWord variable contains any dashes
if (dashReplaced == 'Y')
{
//if the displayWord variable does not
//contain any dashes, the game is over
if (displayWord.find("-", 0) == -1)
{
gameOver = 'Y';
cout << endl << "Yes, the word is "
<< origWord << endl;
cout << "Great guessing!" << endl;
}
else //otherwise, continue guessing
{
cout << endl << "Guess this word: "
<< displayWord << endl;
dashReplaced = 'N';
} //end if
}
else //processed when dashReplaced contains 'N'
{
//add 1 to the number of incorrect guesses
numIncorrect += 1;
//if the number of incorrect guesses is 10,
//the game is over
if (numIncorrect == 10)
{
gameOver = 'Y';
cout << endl << "Sorry, the word is "
<< origWord << endl;
} //end if
} //end if
} //end while
system("pause");
return 0;
} //end of main function
My only edit to the program is directly under the header of Entry Storage and Error Message. I have tried a single for loop, but that simply displayed the error message for every letter entered. Not only that but it displayed it 26 times. Adding a Break command fixed that and it only displayed once. However, it still displayed on every entry.
A member of Cplusplus, pointed out that I was incorrectly testing the same variable against the array in the same location. That is why it displayed the error on every entry. Now with this loop, the error only displays when an entry is entered twice. However, the error message displays all 26 times once more. On top of that, it will only error if the letters are entered one after another.
For example, if I enter A then X then A again, no error is shown. If I enter, A then A again, the error is displayed 26 times. Something is clearly wrong with how the letter variable is being entered into the array on top of the whatever is causing the error message to display multiple times.
Any amount of assistance would be greatly appreciated.
Edit: My professor has gotten back to me and suggested using the following instead of what I have been tinkering with:
for (int x=0; x<5; x++)
if (origWord[x] == letterEntered)
origWord[x] = '-';
Is it just me, or does this miss the mark completely? I haven't tried converting it into my program as a simple copy and paste job produces compile errors. However, I don't see how that does anything with what I'm trying to do.
This set's all entries of your letterGuess array to the most recently guessed letter.
letterGuess[x] = letter;
This isn't what you want.
You need to think about the actual algorithm you need to implement:
The user enters a guess
Check to see if they've already guessed that letter
If they have, display an error message, return to 1.
If they have not, save that guess, continue with the game logic.
If you have already learned about standard containers, this can be trivially done with a std::set, or a std::vector that has been sorted.
You need to compare each element in the array to the guessed word. Best use a for loop for this. No more needs to be said if this is an assignment.
Also don't use system("cls") in your program, it is a massive security flaw and may lose you marks.

while loop, really don't understand

hi im trying to do a while loop, im new to programming and reading online i cant really get my head around it, i have used flag to show that the inputted name matches the name in the data file, i want to do this so that after i know it doesnt match it loops it the whole thing again, i have no clue how to implement this,
{
clrscr();
cout << "This Is The Option To Delete A Record\n";
char yesno;
char search;
char name[21];
int flag = 0;
cout << "Enter Employee Name : ";
Input(name,20);
for (int r=0;r<row;r++)
{
if( strnicmp(name, emp[r].first_name, strlen(name) ) == 0 )
{
flag = 1;
clrscr();
cout << "Employee Number - " << emp[r].employee_number << endl;
cout << "Name - " << emp[r].first_name << " " << emp[r].surname << endl;
cout << "Department Number - " << emp[r].department_number << endl;
cout << "Week Ending Date - " << emp[r].weekend << endl;
cout << "Delete This Record (Y/N)? : ";
Input(yesno);
yesno = tolower(yesno);
if ( yesno == 'y' )
{
emp[r].deleted = true;
cout << "Record Has Been Deleted";
}
else if ( yesno == 'n')
{
cout << "Record Hasn't Been Deleted";
}
}
}
if (flag == 0)
{
cout << "There Are No Matching Records" << endl;
}
pressKey();
}
It's pretty simple, so have a bunch of code you want to keep executing it while a flag is zero, so that's just
int flag = 0;
while (flag == 0)
{
// whole bunch of code
}
That's it, just replace 'whole bunch of code' with the code you've written above.
Implementing this in a while loop would look like this:
bool flag=false;
while(!flag){
...
if(<find a match>) flag=true;
}
Assuming you understand the for loop, I think you can understand the while loop quite easily based on the comparison of for and while.
See, you used a for loop:
for (int r=0;r<row;r++){
// do stuff
}
There are 3 key points here.
int r=0 This is your initial condition.
r<row This is your condition which keeps the loop running.
r++ This is what happens at the end of each iteration of loop.
To rephrase the statements above:
Considering r equals zero initially, while r is less than row, increment r.
Now we can easily see how while loop is striking us:) To implement this, consider the following while loop example:
int r=0; //(1)
while(r<row){ //(2)
//do stuff
r++; //(3)
}
See, now the 2 loops do practically the same thing.
If you want to do operations based on a flag, you can also prefer an infinite loop:
while(1==1){
if(some condition)
break;
}
as well as an infinite for loop:
for(;;){
if(if some condition)
break;
}
Again, 2 loops are practically the same.
so basically, you have a file with some data. And also, you accept some data from the user.
And then you perform a comparison between the appropriate fields of the two sets.
Why would you want to do it all over again once the entire comparison (file process) is done?
if you simply want to run an infinite loop, you can do this:
while(true)
{
//your code
}
you can do same with a for loop also. infact for loop and while loop both are same except for the syntax. i.e. an infinite for loop.
for (int r=0;r<row;r++)
{
if(r==row-1)
{
r=0;
}
}
I guess what you want to do is to, once one set of user input doesn't match the file content, you want to take another set and match it again and so on.
so you don't need an infinite or always executing loop for this.
Just make your comparison module a separate function which should accept the set of user inputs. All you do is accept user inputs and show the result. And give the user an option to re-enter inputs.
Below is simple algo for what you want.
int main()
{
char a='a';
while(a != '~')
{
TakeUserInput();
if(PerformComparison())
{
cout << "Success";
break;
}
}
}
inside TakeUserInput() you do all those cin << to set a global array or set of global variable. also, you cin << a, to terminate program at your will.
and inside PerformComparison(), you do what you have posted here in your question.