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

Time:11-13

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;
}

CodePudding user response:

Here is a modified version of your inner loop:

            // Test for not palindrome
            bool is_palindrome = true;
            int count = 0;
            int countback = text_length - 1;
            while (is_palindrome && count < countback) {
                unsigned char c1 = text[count  ];
                if (isalnum(c1)) {
                    unsigned char c2 = text[countback--];
                    if (isalnum(c2)) {
                        is_palindrome = (toupper(c1) == toupper(c2));
                    } else {
                        count--;
                    }
                }
            }

Here is a function version (almost) without redundant tests:

#include <ctype.h>
#include <stdbool.h>
#include <string.h>

bool is_palindrome(const char *text) {
    size_t i = 0, j = strlen(text);
    unsigned char c1, c2;
    while (i < j) {
        while (!isalnum(c1 = text[i  ])) {
            if (i == j)
                return true;
        }
        while (!isalnum(c2 = text[--j])) {
            if (i == j)
                return true;
        }
        if (toupper(c1) != toupper(c2))
            return false;
    }
    return true;
}

CodePudding user response:

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") ) ;
  • Related