arrayscspacepalindromepunctuation

How to discard all characters other than a-z && A-Z && 0-9 in user input to find possible palindrome


What does program solve:
Find palindrome from user input.

What issue to tweak:
How to make code accept sentence like Rise to vote sir! as Palindrome if we disregard punctuation and blank spaces. If you read it backwards it will be the same!

Things that I have tried: I tried to just save characters a-z, A-Z, and 0-9 into character-type array then proceed with the logic on it. But to no avail.

I wish if someone could suggest where to tweak the following C snippet, in a way it can handle phrase or sentence to not include spaces, punctuation, and special characters within the computation of the code logic.

/* Search for a palindrome */

#include <stdio.h>
#include <ctype.h>

#define EOL '\n'
#define TRUE 1
#define FALSE 0

int main()
{
    int tag, count, countback, flag, loop = TRUE;
    char letter[80];

    /* main loop */
    while (loop)
    {
        /* anticipated palindrome */
        flag = TRUE;

        /* read in the text */
        printf("\nPlease enter a word, phrase, or sentence below:\n");
        for (count = 0; (letter[count] = getchar()) != EOL; ++count)
            ;

        /* test for end of program keyword END */
        if ((toupper(letter[0]) == 'E') && \
            (toupper(letter[1]) == 'N') && \
            (toupper(letter[2]) == 'D'))
            break;

        tag = count - 1;

        /* carry out the search */
        for ((count = 0, countback = tag); count <= tag / 2; (++count, --countback))
        {
            if (letter[count] != letter[countback])
            {
                flag = FALSE;
                break;
            }
        }

        /* display message */
        for (count = 0; count <= tag; ++count)
            putchar(letter[count]);

        if (flag)
            printf(" --> IS a Palindrome!\n\n");
        else
            printf(" --> is NOT a Palindrome.\n\n");
    }   /* end of main loop */

    return 0;
}

Solution

  • In the loop, add sub-loops such that while !isalnum(letter[count]) and !isalnum(letter[countback]) are true; increment count and decrement countback respectively. e.g.:

    // skip non-alphanum on left
    while( !isalnum(letter[count]) ) 
    {
        count++ ;
    }
    
    // skip non-alphanum on right
    while( !isalnum(letter[countback]) ) 
    {
        countback-- ;
    }
    

    However doing that breaks your outer loop because you can no longer make the assumption that the centre of the string is the centre of the possible palindrome. It is better in any case to simply test that count > countback. e.g:

    for( int count = 0, countback = tag ; 
         count < countback; 
         ++count, --countback )
    

    and because you have then modified the control variables in the loop, you need to add a check in the "not palindrome" test, and you also want to ignore case:

    if( count < countback && 
        toupper(letter[count]) != 
        toupper(letter[countback])
    {
        flag = FALSE ;
    }
    

    That test can in fact be simplified:

    if( count < countback )
    {
        flag = (toupper(letter[count]) ==
                toupper(letter[countback]) ;
    }
    

    To avoid undefined behaviour of the ctype.h functions, the letter array should have type unsigned char also.

    That is the minimal "tweak" as you asked. More of a comment than part of the answer, but I would suggest some "style" improvements too on structure, commenting, naming, scope and I/O; my re-implementation of your code for what it is worth :

    // Test for a palindrome
    #include <stdbool.h>
    #include <string.h>
    #include <stdio.h>
    #include <ctype.h>
    
    
    int main()
    {
        bool terminate = false ;
        
        while( !terminate )
        {
            // read in the text
            unsigned char text[80] ;
            printf("\nPlease enter a word, phrase, or sentence below:\n");
            fgets( text, sizeof(text), stdin ) ;
            int text_length = strlen(text) ;
            if( text[text_length-1] == '\n')
            {
                text_length-- ;
                text[text_length] = 0 ;
            }
    
            // test for end of program keyword END
            terminate = text_length == 3 &&
                        toupper(text[0]) == 'E' &&
                        toupper(text[1]) == 'N' &&
                        toupper(text[2]) == 'D' ;
                        
            if( !terminate )
            {
        
                // Test for not palindrome
                bool is_palindrome = true ;
                for( int count = 0, countback = text_length - 1 ; 
                     is_palindrome && count < countback; 
                     count++, countback-- )
                {
                    // skip non-alphanum on left
                    while( count < countback && 
                           !isalnum(text[count]) ) 
                    {
                        count++ ;
                    }
                    
                    // skip non-alphanum on right
                    while( count_back > count &&
                           !isalnum(text[countback]) ) 
                    {
                        countback-- ;
                    }
                    
                    // If not at or past middle...
                    if( count < countback )
                    {
                        // Test for left/right match
                        is_palindrome = (toupper(text[count]) == 
                                         toupper(text[countback])) ;
                    }
                }
        
                // Output test and test result.
                printf( "%s --> %s a Palindrome!\n\n", 
                        text, 
                        (is_palindrome ? "IS" : "IS NOT") ) ;
            }
        }
    
        return 0;
    }
    

    This also allows strings such as "end Ådne" and "end of the world" to be tested without terminating as yours would.

    Further it is always a good idea to separate processing from I/O. To that end you might create a function:

    bool isPalindrome( const char* str )
    {
        const unsigned char* text = (const unsigned char*)str ;
        size_t text_length = strlen(str) ; 
        if( text_length == 0u ) return true ;
        
        // Test for not palindrome
        bool is_palindrome = true ;
        for( size_t count = 0, countback = text_length - 1; 
             is_palindrome && count < countback; 
             count++, countback-- )
        {
            // skip non-alphanum on left
            while( count < countback &&
                   !isalnum(text[count]) ) 
            {
                count++ ;
            }
            
            // skip non-alphanum on right
            while( countback > count &&
                   !isalnum(text[countback]) ) 
            {
                countback-- ;
            }
            
            // If not at or past middle...
            if( count < countback )
            {
                // Test for left/right match
                is_palindrome = (toupper(text[count]) ==
                                 toupper(text[countback])) ;
            }
        }
    
        return( is_palindrome ) ;
    }
    

    then in the main body you would simply have:

    bool is_palindrome = isPalindrome( text ) ;
    printf("%s --> %s a Palindrome!\n\n", text, (is_palindrome ? "IS" : "IS NOT"));
    

    or even:

    printf( "%s --> %s a Palindrome!\n\n", text, 
            (isPalindrome( text ) ? "IS" : "IS NOT") ) ;