Why the do\while loop is breaking and Terminal is hanged? - c++

I have the following code and it somehow verify if the input is number, but the error occurs while looping back to ask the user for the second time to enter the number.
I can't figure it out.
#include <iostream>
#include <limits>
using namespace std;
void botVarification();
void userDecision();
bool isHuman(string humanInput);
int main()
{
botVarification();
return 0;
}
void botVarification()
{
string humanInput;
cout << "Enter the number to verify that you are not a bot: ";
cin >> humanInput;
bool isHuman = true;
do {
for (int humanInputvalidator; humanInputvalidator < humanInput.length() && isHuman; humanInputvalidator++) {
if (!(humanInput[humanInputvalidator] >= 48 && humanInput[humanInputvalidator] <= 57)) {
isHuman = false;
cout << "Not success, try again!";
cin >> humanInput;
}
else
cout << "Success" << '\n';
break;
}
} while (!(isHuman));
}

$ clang++-7 -Wall -o main main.cpp
main.cpp:23:39: warning: variable 'humanInputvalidator' is
uninitialized when used here [-Wuninitialized]
for (int humanInputvalidator; humanInputvalidator < hum...
^~~~~~~~~~~~~~~~~~~
main.cpp:23:37: note: initialize the variable 'humanInputvalidator' to
silence this warning
for (int humanInputvalidator; humanInputvalidator < hum...
^
= 0
1 warning generated.
So let's fix that.
Additionally, on the second iteration of getting input from the user, humanInputvalidator < humanInput.length() && isHuman will always be false. You'll want to reinitialize isHuman = true inside the do while loop.
Functions are a great way to simplify your code!
Use functions! It's a great way to simplify your code and decrease the number of variables in any single context.
bool isThisHumanInput(std::string data) {
for (int i = 0; i < data.length(); i++)
if (!(data[i] >= 48 && data[i] <= 57))
return false;
return true;
}
void botVarification()
{
string humanInput;
bool isHuman;
do {
cout << "Enter the number to verify that you are not a bot: ";
cin >> humanInput;
isHuman = isThisHumanInput(humanInput);
if (isHuman) {
cout << "Success" << '\n';
} else {
cout << "Not success, try again!\n";
}
} while (!isHuman);
}

There's several changes that are necessary to get this code functioning correctly. Here's one working version, with the functions and includes that you don't actually use yet removed and a typo corrected.
Highlights: We have to initialize humanInputvalidator, we have to reset isHuman to true at the beginning of the do-while loop, and the logic for exiting the inner for loop and printing "Success!" was all changed.
#include <iostream>
#include <string>
void botVerification();
int main()
{
botVerification();
return 0;
}
void botVerification()
{
std::string humanInput;
std::cout << "Enter the number to verify that you are not a bot: ";
std::getline(std::cin, humanInput); // getline to capture whitespace
bool isHuman;
do {
isHuman = true; // Reset to true at the beginning of each loop
// Initialize humanInputvalidator
for (int humanInputvalidator = 0; humanInputvalidator < humanInput.length() && isHuman; humanInputvalidator++) {
// Rewrite comparison using char literals for clarity
if (!(humanInput[humanInputvalidator] >= '0' && humanInput[humanInputvalidator] <= '9')) {
isHuman = false;
std::cout << "Not success, try again! ";
std::getline(std::cin, humanInput);
// No need to break since we test isHuman in the for loop
}
}
} while (!(isHuman));
// Print success after input is fully verified, not at some intermediate stage
std::cout << "Success!\n";
}

First of all, you need to initialize humanInputvalidator when you define it in the for loop. Variables in C++ do not have default values.
Also, in case you find an incorrect character in humanInput, you make the user input again. This happens while you are iterating over the characters in the string. This is not really good. In this particular case it does not break, but it may in some other one, so keep that in mind.
Moreover, in the for loop you alway break on the first iteration. Your break statement is just below the if-else and I think your intention was that it be inside the else branch.
All in all, I cannot pinpoint any specific reason for the infinite loop you are getting but these are at least a few things you can fix and then see what happens. I would also suggest to simplify your code and make it more readable, by splitting out the invalid input check to a separate function, like this:
bool isInputValid(const string& humanInput)
{
for (int humanInputvalidator = 0; humanInputvalidator < humanInput.length(); humanInputvalidator++) {
if (!(humanInput[humanInputvalidator] >= 48 && humanInput[humanInputvalidator] <= 57)) {
return false;
}
}
return true;
}
void botVarification()
{
string humanInput;
cout << "Enter the number to verify that you are not a bot: ";
cin >> humanInput;
do {
if (isInputValid(humanInput))
{
cout << "Success" << '\n';
break;
}
cout << "Not success, try again!";
cin >> humanInput;
} while (true);
}

Related

Why is my C++ function skipping my IF statements?

So, let me preface that I am still learning C++ and would appreciate some guidance on what I am doing wrong.
My prompt is to write a function that continuously prompts a user for a valid age (between 0 and
100) and the function must only return the age to the caller of the function after a valid age is retrieved. AND For each function, you must declare the function using a function prototype before main and then define the function after main.
Here is my code,
#include <iostream>
using namespace std;
int num;
bool valid;
int validateInput()
{
cout << "Pick a number between 0 and 100" << endl;
cin >> num;
while(bool valid = false)
{
if(num <= 0)
{
cout << "Error: Number is invalid because it's too low" << endl;
bool valid = false;
return 0;
}
else if (num >= 100)
{
cout << "Error: Number is invalid because it's too high" << endl;
bool valid = false;
return 0;
}
else
{
cout << "You are " << num << " years old." << endl;
bool valid = true;
return 0;
}
}
}
int main()
{
validateInput();
return 0;
}
So I am trying to get my program to work but the IF statements keep getting skipped.
Am I misunderstanding something? Any and all help is very much appreciated.
EDIT: Thank you to Arvin and iammilind for your help.
I was able to fix the code so my while loop condition would actually trigger, moved my cout statements into the loop and so I wouldn't get infinite output.
My final working code looked like this.
#include <iostream>
using namespace std;
int num;
bool valid = false;
int validateInput()
{
while(!valid)
{
cout << "Pick a number between 0 and 100" << endl;
cin >> num;
if(num <= 0)
{
cout << "Error: Number is invalid because it's too low" << endl << endl;
bool valid = false;
}
else if (num >= 100)
{
cout << "Error: Number is invalid because it's too high" << endl << endl;
bool valid = false;
}
else
{
cout << "You are " << num << " years old." << endl;
bool valid = true;
return 0;
}
}
}
int main()
{
validateInput();
return 0;
}
You give a false value to the while loop that makes the while loop doesn't start the loop.
do this instead:
bool valid = false;
while (!valid){ // while valid is still false, do the loop
// your code here
}
Further explanation: You're currently using sentinel-controller loop
reference: https://simplecplusplus.wordpress.com/tag/sentinel-controlled-loop/
In order for while loop to start running, you've to provide the "true" condition to the while, and it will start looping until the condition turn out to false.
Programming tips for you: next time, you've to see the larger picture of your code every time you're trying to debugging. If you're sure the if-else code is running and have no problem, you have to enlarge your investigation for the bug, maybe it's the while loop that didn't work, not if-else. And if the while loop seems having no problem, maybe its the function or the caller of the function
while(bool valid = false) never allows the execution to enter the loop. Hence the if conditions are never called.
Use it as below:
while(valid == false) { // see '==' sign. `while(not valid)` is also fine
// ... your 'if' conditions
}
Actually you are creating a locally scope variable within while() by having a bool before it. So bool valid hides bool ::valid (declared outside).
Also, once the loop ends, you may want to reset it to false again. Otherwise this function will never be able to used again!
Using globals (bool valid) for such functionality is a bad design.

Visual Studio shows warning C6330: 'char' passed as _Param_(1) when 'unsigned char' is required in call to 'isdigit'. when I try to build

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.

Why isn't the program outputting true regardless of case?

My assignments requires me to keep accepting input from the user and output whether or not it is a palindrome until the word DONE is inputed.
Also, words like Bob must have an output of true because we must disregard case (upper/lower.)
This is my first time using C++.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string wordInput;
while (wordInput != "DONE")
{
cout << "Please enter a word: ";
cin >> wordInput;
int wordLength = wordInput.length();
int wordHalf = (wordLength / 2);
bool flag = false;
for (int i = 0; i <wordHalf; i++)
{
if (tolower((wordInput[i]) == tolower(wordInput[wordLength-1-i])))
{
flag = true;
}
else
{
flag = false;
break;
}
}
if (flag == true)
{
cout << "true"<<endl;
}
else
{
cout << "false"<<endl;
}
}
return 0;
}
It might have something to do with 'wordInput' being declared twice, once right before the while loop and once within it. It is mixing up what the while loops condition is looking at.
Your issue with words not being correctly identified comes from this line:
if(tolower((wordInput[i]) == tolower(wordInput[wordLength-1-i])))
If you look carefully, you set the parentheses incorrectly. Try this instead:
if(tolower(wordInput[i]) == tolower(wordInput[wordLength-1-i]))

I'm having difficulty with my do while loop. It only executes once

The battleship program I wrote is supposed to loop a infinite amount of times until a condition is met. However it stops after the first execution. What is the matter with my loop? The code runs, however towards the end it outputs game over twice when its not supposed to. There are still more ships remaining in the game.
#include<iostream>
#include<fstream>
#include<string>
#include<cmath>
using namespace std;
bool FleetSunk();
void Fire(int&, int&, char&);
char ocean[25][25];
int main()
{
int x;
int y;
char spot;
ifstream input;
input.open("ocean.txt");
for(x=0;x<25;x++)
{
for(y=0;y<25;y++)
{
input >> ocean[x][y];
}
}
FleetSunk == false;
do
{
cout << "Please input x coordinate: ";
cin >> x;
cout << endl << "Please input y coordinate: ";
cin >> y;
cout<< endl;
Fire(x,y,spot);
FleetSunk();
}while(FleetSunk() == false);
}
void Fire(int& x, int&y, char&spot)
{
spot = ocean[x][y];
if(spot == '#')
{
cout << "You Have hit a ship"<< endl;
ocean[x][y] = 'H';
}
else if(spot == 'H')
{
cout << "HIT AGAIN" << endl;
}
else if(spot == '-')
{
cout << "MISS" << endl;
}
}
bool FleetSunk()
{
int m;
int n;
for(m=0;m < 25; m++)
{
for(n=0;n<25;n++)
{
if(ocean[m][n] == '#')
{
cout << "You still have ships remaining" << endl;
}
else
{
cout<< "You have hit all the ships. GAME OVER!" << endl;
return true;
}
}
}
}
Your FleetSunk function has two problems. One should have been a compiler warning, and one is a logical problem.
Firstly, not all paths in the function return a value... Technically. Although in your case that's not quite true because of the logic problem. Let me be more specific: If your entire board is populated with '#', and no shots are taken, then the function behaviour is undefined. It completes the loop and then does not return a value.
So now let's do the logic. You cannot know whether there are any un-hit ship locations until you have examined the entire board. That means you cannot exit from your inner loop in the way you are doing. How about instead you return false if you encounter an "alive" position, and return true at the end of the function (which is only reached if you never encounter an "alive" position).
bool FleetSunk()
{
for( int m = 0; m < 25; m++ )
{
for( int n = 0; n < 25; n++ )
{
if( ocean[m][n] == '#' ) return false;
}
}
return true;
}
See in the comments under your question for other suggestions related to how you are calling FleetSunk in your loop. I also recommend (as evident in my code example) that you don't write stuff to cout in a function that is testing for some condition. It's the responsibility of the caller to do that, not the function itself.
Finally, I would just like to say that the line FleetSunk == false; above your loop really does not do what you might think. That will take the function pointer, convert it to boolean and compare it with false. It's a crazy thing to do, and also useless because the resulting value isn't used for anything. Just delete that line.

Checking for letters when reading an integer

I'm making an app that requires the user to input a production order (7 digits long) like this:
int order = 0;
cout << "Insert the order number: ";
cin >> ordem;
How can I prevent the user from entering a letter? Like "I2345G789"?
Doing that, my app just enters an infinite loop. I was thinking to use a function like this:
bool isLetter(int a)
{
string s = to_string(a);
for (int i = 0; i < s.size()-1; i++)
{
if (isdigit(s[i]))
{
return false;
}
else
return true;
}
}
And then:
if (isLetter(order))
{
cout << "Insert only numbers \n";
}
But it doesn't work. Why? And how can I improve the code?
PS: I'm very new to programming, so, sorry for any beginner mistakes.
I guess you have a loop around your code in order to ask for the order number again in case it contains non-digits, for example:
while(...)
{
int order = 0;
cout << "Insert the order number: ";
cin >> order;
}
If you enter something that cannot be parsed into an integer, then the input stream will go into failure mode and that might be the reason why you end up in an infinite loop. In order to overcome your problem in a simple way, you could read a string instead:
string order;
while (true)
{
cout << "Insert the order number: ";
cin >> order;
if (isLetter(order))
cout << "Insert only numbers" << endl;
else
break;
}
The function isLetter() now takes a string and looks like this:
bool isLetter(string s)
{
// Return true if the given string contains at least one letter.
for (size_t i = 0; i < s.size(); i++)
if (!isdigit(s[i]))
return true;
// Return false if there are only digits in the given string.
return false;
}
Please note, that it should be i < s.size() and not i < s.size()-1. And maybe you should rename your function isLetter() to hasLetter(), because that would be a bit more correct.