Thomas C
Thomas C

Reputation: 41

Palindrome Checker Code Stuck in Infinite Loop

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

Answers (3)

Francis Cugler
Francis Cugler

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

Empty
Empty

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

Sourabh Singh
Sourabh Singh

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

Related Questions