Reputation: 41
I'm having an issue where I'm designing a palindrome checker. I have no problem with single words ("noon", "2002", etc.), but every time when I type in a phrase that has multiple words with spaces (such as "laminate pet animal"), my program loses its mind and goes into an infinite loop. Perhaps it has something to do with the checks (Make sure the string isn't NULL or greater than 80 characters) I put inside? I've been step by step debugging with no success. I think it has something to do with how the string is stored in memory, but I can't place it exactly.
//Preprocessor directives
#include <iostream>
#include <cstdlib>
#include <string>
#include <iterator>
#include <vector>
#include <stdlib.h>
using namespace std;
//Function declarations
string input();
bool check(string a);
int main()
{
//Repeater variable
bool rep = 1;
bool result = 0;
//Declares string to be checked
string palin;
while (rep == 1)
{
//Creates string and calls input function
palin = input();
//Close function if stopped midway
if (palin == "2")
return 0;
result = check(palin);
//Displays the results
if (result == 1)
cout << palin << " is a palindrome." << endl;
else
cout << palin << " is not a palindrome." << endl;
//Ask if the user wants to enter another Palindrome
cout << "Continue? (1 for yes, 0 for no): ";
cin >> rep;
}
cout << "Closing program..." << endl;
system("pause");
return 0;
}
string input()
{
//Asks for and receives input string
string temp;
cout << "Please enter a string (type zero or 0 to quit): ";
cin >> temp;
//Close program if user enters 0 or zero
if (temp == "0" || temp == "zero")
{
cout << "Exiting program..." << endl;
system("pause");
return "2";
}
//Check if string is null, then ask for input again
if (temp.empty())
{
cout << "There is nothing entered. Please enter a string: ";
cin >> temp;
}
//Check if string is too long, ask for input again
if (temp.length() >= 80)
{
while (temp.length() > 80)
{
cout << "The string is too long. Please enter a smaller string: ";
cin >> temp;
}
}
return temp;
}
bool check(string a)
{
//Creates 2 iterators that traverse the string
string::iterator test1;
string::reverse_iterator test2;
test1 = a.begin();
test2 = a.rbegin();
//Continue until the end of either side of the string
while (test1 != a.end() && test2 != a.rend())
{
//Check if the current symbol is alphanumeric
while (test2 != a.rend() && !isalnum(*test2))
++test2;
while (test1 != a.end() && !isalnum(*test1))
++test1;
//Break loop when you hit the end
if (test1 == a.end() || test2 == a.rend())
break;
//If they're not the same it's not a palindrome, exit function
if (tolower(*test1) != tolower(*test2))
return 0;
++test1;
++test2;
}
return 1;
}
Upvotes: 0
Views: 311
Reputation: 7925
I feel that you are over complicating this code: I will show you a simplified version within a few lines of code. The code is clean, concise, readable and very expressive; it will do exactly what it says it will do. Then I'll explain where you went wrong finishing up with a description of my implementation while using an appropriate tool or algorithm to get the job done:
#include <algorithm>
#include <iostream>
#include <string>
void runPalindrome();
int main() {
runPalindrome();
return 0;
}
void runPalindrome() {
std::string quit;
do {
std::cout << "Please enter text to test if it is a palindrome:\n";
std::string input;
std::getline(std::cin, input, '\n');
std::string checker(input);
std::reverse(checker.begin(), checker.end());
if (input == checker)
std::cout << input << " is a palindrome!\n";
else
std::cout << input << " is not a palindrome!\n";
std::cout << "Press Q or q to (Q)uit or any other character to continue...\n";
std::getline(std::cin, quit, '\n');
} while ( quit != std::string( "Q" ) && quit != std::string( "q" ) );
std::cout << "Exiting the program!\n";
}
The issue you was having is that you were using std::cin >> variable
and this will take in text up to the first white space character
it sees. The rest of the text on that line is still in the buffer but isn't stored into your variable. Here you need to use std::getline()
and it takes at least two parameters, the first being the source of the input such as std::cin
or std::ifstream
or std::istringstream
etc. the second parameter is the variable that you want to store your information.
There is a third parameter that is optional and in this case we do want to use it. The third parameters looks for a delimiter and in this case we want to look for the first newline character '\n'
. The reason we want to use this here is that it will retrieve it from the iostream buffer but it will not store it into your string. This is good for when we check to see if it is a palindrome.
Once we get the string of text that the user entered we then make a std::string
variable named checker
and initialize it with the original input. We want a direct copy because there is an algorithm that is found in the algorithm
header which is called std::reverse
and for our purposes this is perfect!
However we need that copy since std::reverse
will do the operations in place. We don't want to lose our original string. So now our checker will be in the reverse order of the original input. Then it is a simple matter of a single comparison to see if the two strings are equal, if they are display the appropriate message and if not do the same. Finally we print a message asking if the user wants to quit or not.
EDIT: -- Note: - I had forgotten or overlooked a simple thing about this program, the palindrome in the above is case
sensitive
, we could do one of three things, we could first leave it as is to where we are expecting the fact that 'A' != 'a'
. We could fix this by converting all alpha to either ::toupper
or ::tolower
removing all case sensitivity, however these functions work on single characters and not a full string, so we would have to write a function to make all the characters in the string either all uppercase or all lowercase or call another nice algorithm by the stl and that is std::transform(...)
. Again std::transform()
is found in the Algorithm
library. Last but not least, we could give the user a choice between both versions. I'll leave this part up to you as an exercise.
-Example- std::transform
{
std::string str( "HelLo" );
std::cout << str << '\n';
std::transform( str.begin(), str.end(), str.begin(), ::toupper );
std::cout << str << '\n';
std::transform( str.begin(), str.end(), str.begin(), ::tolower );
std::cout << str << '\n';
}
Upvotes: 0
Reputation: 516
std::cin
's >>
operator only reads up to the next whitespace character. If you want to read the entire line, use std::getline()
.
cin >> temp; //reads until the next whitespace
getline(cin, temp); //reads until the next newline character
What was happening was that the first read operation after you enter "race car" would read "race" and then leave "car" in the stream, then the next read operation would read "car", resulting in unexpected behavior since your code was expecting a 1 or 0.
This is unrelated to your problem, but it's usually good form to not use using namespace std
. There's a variety of reasons, but the most basic one is if you write your own function called getline()
, you'll run into problems.
Upvotes: 3
Reputation: 1
Replace cin >> temp;
with getline(cin, temp);
to get whitespace seperated strings and after cin >> rep;
add cin.ignore();
to flush the newline.
Upvotes: 0