Link to home
Start Free TrialLog in
Avatar of bud6204
bud6204

asked on

Reversing only the characters in a string of multiple words and/or spaces

I have been working on this project for a couple weeks and am having trouble figuring out how to deal with punctuation and with spaces at the end of a phrase. The project contains several files: main.c,  tools.c, tools.h and tests.c. The function that I believe is causing the issue is the words_initialize function in tools.c. The problem is when I have a phrase "spaces at end<space><space>" the program should reverse the characters only resulting in "secaps ta dne<space><space>" but what I get is "secaps ta dne" without the trailing spaces. I know why it is doing this but I don't know how to make it not do it. Any help would be appreciated.

Here is the code:

MAIN.C
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tools.h"

/*
    remove when CLUE is accepting beyond ANSI
    Maintaining Backwards compatibility
*/
extern char* strdup( const char*);

/*
   See tests.c for details on the required functions and data structures
*/
extern const int NB_TESTS;
extern const char * tests_inputs[];
extern const char * tests_expected[];


/* Forward definition of prototype, defined at the end of this source file */
void runAllTests();

/* MAIN starts here  */
int main()
{
    printf("\tWelcome to PA202\n\n");
    runAllTests();

    return EXIT_SUCCESS;
}


void runAllTests(){
    /* ROLE       Runs all the tests specified and displays results
    */
    int i;
    char *test_input       		= NULL; 	/* what we provide our function with */
    const char *test_expected   = NULL;  	/* what we expect the result to be */
    char *test_observed    		= NULL; 	/* what our function returns */
    int test_outcome;              			/* test successful or not? */

    /* let's iterate over all available tests */
    for(i=0; i < NB_TESTS; i++){
            printf("Test #%d\t\t", i);

            /* setting up this specific test */
            test_input = (char*)strdup(tests_inputs[i]);
            test_expected = tests_expected[i];

            /* getting the result from our implementations */
            words_modify(test_input);

            /* test_input has been modified */
            test_observed = test_input;

            if (!test_observed){
                printf("FAILED\n");
                continue;
            }

            test_outcome = strcmp(test_observed, test_expected);

            /* verifying if observed result is the expected one */
            if ( test_outcome != 0){
                   /* if expected output is not the produced one, test fails*/
                   printf("FAILED");
            }else{
                    /* otherwise test was successfull */
                    printf("OK");
            }
            printf("\t\t[%s]->[%s]", tests_inputs[i], test_observed);
            printf("\n");

            /* de-allocating memory allocated by words_modify */
            if (test_observed) free(test_observed);
			test_input = NULL; 	/* test input is now a dangling pointer */
    }
}

Open in new window


TOOLS.H

#ifndef _TOOLS_H_
#define _TOOLS_H_

void words_modify( char* str );
    /* 	ROLE        Parses the string str and identify each word in it.
                    Words are separated by any character which makes the
                    is_separator function returns true.
                    The address of the first character of each word in
                    the string str, is stored as a pointer in the array words.
                    When we find the end of the word we just added in the string
                    str, we modify it so that the next character becomes '\0'.
                    We are not able to handle more than maxwords words
                    and subsequent ones will be ignored.
                    Our function will return the number of words it found
                    which also indicates how many of the maxwords elements in
                    the array words are meaningful.
		RETURNS     the number of words identified in the string input.
		PARAMETERS  input    - string in which we will identify words
                    words    - an array of pointer on strings which will be the
                               words we have identified
                    maxwords - the size of the words array of pointers
    */
#endif

Open in new window


TOOLS.C
#include <stdlib.h>
#include <ctype.h>


void word_reverse( char* str ){
     /* ROLE:       [ Reverses the order of the characters in the string. Does
                      not modify the end of string marker '\0'. ]
        RETURNS:    [ nothing - void ]
        PARAMETERS: [ str - the string to reverse ]
        AUTHOR:     [ REMOVED ]
        DATE:       [ 3/19/12 ]
     */

     char hold;      // temporary location used to swap characters in the string
     char *end_ptr;      // char pointer used to swap characters in the string

     // set char pointer to str
     end_ptr = str;


     // Loop through all characters in string
     while(*end_ptr){
        end_ptr++;
     }

     // Set end pointer to character just before string terminator '\0'
     --end_ptr;

     /* Loop through half of string swapping the first character with the last
        character, then the second with the next to last, etc., excluding the string-
        terminating character '\0'. If the string has and odd number of characters,
        the middle character does not change.*/

     while(str < end_ptr){
         hold = *str;
         *str = *end_ptr;
         *end_ptr = hold;

         str++;
         end_ptr--;
     } // end while
} // end function word_reverse



int is_separator( char data ){
    /* ROLE:        [ Tests if the character passed as parameter is a separator;
                    i.e. something different than a letter or digit. ]
       RETURNS:     [ 1 - if data is a separator
                      0 - otherwize ]
       PARAMETERS:  [ data - the character to test ]
       AUTHOR:      [ REMOVED ]
       DATE:        [ 3/19/12 ]
    */

    return(!isalnum(data));

} // end function is_separator

int words_initialize( char* str, char* words[], int maxwords ){
    /* 	ROLE:       [ Parses the string str and identify each word in it.
                      Words are separated by any character which makes the
                      is_separator function return true.
                      The address of the first character of each word in
                      the string str, is stored as a pointer in the array words.
                      When we find the end of the word we just added in the string
                      str, we modify it so that the next character becomes '\0'.
                      We are not able to handle more than maxwords words
                      and subsequent ones will be ignored.
                      Our function will return the number of words it found
                      which also indicates how many of the maxwords elements in
                      the array words are meaningful. ]

		RETURNS:    [ the number of words identified in the string input ]
		PARAMETERS: [ str      - string in which we will identify words
                      words    - an array of pointers to strings which will be the
                                 words we have identified
                      maxwords - the size of the words array of pointers ]
        AUTHOR:      [ REMOVED ]
        DATE:        [ 3/19/12 ]
    */

    int nbwords = 0; /* total number of words in str;
                        also indicates how many of the maxwords elements in
                        the array words are meaningful.
                     */

    int state = 0;   /* state = 0 means we're looking for the start of a word
                        state = 1 means we're looking for the end of a word
                     */

    // Loop through all characters in the string
    while(*str){

        // When we are outside a word (state == 0)
        if (state == 0){

            // The first non-separator character...
            if (!is_separator(*str)){

                if( nbwords < maxwords)
                   // ...is the beginning of the first word
                   words[nbwords++] = str;
                else
                   // If we have exceeded maxwords, break out of the loop, ignoring all subsequent words
                   break;

                // We are now inside a word
                state = 1;
            } // end if
        } // end if
        else {

            // When we are inside a word (state == 1), the first separator character...
            if (is_separator(*str)) {
                // ...is the end of the word
                *str = '\0';
                // We are now outside of a word
                state = 0;
            } // end if
        } // end else

        // Move to the next character in the string
        str++;
    } // end while

    return nbwords;
} // end function words_initialize


void word_handle_marker(char* str){  // WORKING FUNCTION - JUST DOCUMENT :)  - GIFT
     /* ROLE:        [ Replaces the end of string marker '\0' by a space ' ' ]
        RETURNS:     [ nothing - void ]
        PARAMETERS:  [ str - the string to modify ]
        AUTHOR:      [ REMOVED ]
        DATE:        [ 3/19/12 ]
     */

     // Loop through all characters in the string
     while(*str){
        str++;
     } // end while

     /* Set the separator character changed to '\0' by
        function words_initialize to a space ' '
     */
     *str = ' ';
} // end function word_handle_marker

void words_modify(char* str, int* key){
     /* ROLE:        [ Modifies a string by calling functions words_initialize,
                       words_reverse, and words_handle_marker]
        RETURNS:     [ nothing - void ]
        PARAMETERS:  [ str - the string to modify ]
        AUTHOR:      [ REMOVED ]
        DATE:        [ 3/19/12 ]
     */

     // Maximum number of words that can be initialized by function words_initialize
     const int MAXWORDS = 256;
     // array of pointers to characters to be used in function words_initalize
     char* allwords[MAXWORDS];

     int i;         // loop counter
     int nbwords;   // number of words in string

     // initialize all pointers in allwords to NULL
     for(i=0; i < MAXWORDS ; i++)
              allwords[i] = NULL;

    // Get number of words in string
    nbwords = words_initialize(str, allwords, MAXWORDS);

    // Reverse all the words in the string
    for(i=0 ; i < nbwords ; i++){
            word_reverse(allwords[i]);
    }

    // Scramble all the words in the string
    for(i=0 ; i < nbwords ; i++)
            word_scramble(allwords[i], (int)key);


    /* Set separator characters modified to string terminators '\0'
       in words_initialize to spaces
    */
    for(i=0 ; i < (nbwords-1) ; i++)
            word_handle_marker(allwords[i]);

} // end function words_modify

Open in new window


TESTS.C
#include <stdlib.h>

const int NB_TESTS = 19;


const char *tests_inputs[] = {
    "hello",                    // single word with odd number of characters
    "test",                     // single word with even number of characters
    "how are you",              // multiple words with one space between words
    "several   spaces",         // several spaces in a row between words
    " space in front",          // one spaces before any words
    "  spaces in front",        // several spaces before any words
    "space at end ",            // one space at the end
    "spaces at end   ",         // several spaces at the end
    "tab    control",           // tab control between words
    " ",                        // space only
    "    ",                     // several spaces only
    "",                         // empty string
    "?punctuation",             // single punctuation mark at beginning of word
    "punctuation?",             // single punctuation mark at end of word
    "punct.uation",             // single punctuation mark in the middle of word
    "?#.@*punctuation",         // mutiple punctuation marks at beginning of word
    "punctuation!@#$%",         // mutiple punctuation marks at the end of word
    "?@p unctuation.% #multi",   // multiple punctuation marks

    // 257 words - tests that words_initialize does not attempt to handle more than MAXWORDS
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This is a test "
    "This is a test This is a test This is a test This is a test This should not be reached"

};


const char *tests_expected[] = {
    "olleh",
    "tset",
    "woh era uoy",
    "lareves   secaps",
    " ecaps ni tnorf",
    "  secaps ni tnorf",
    "ecaps ta dne ",
    "secaps ta dne   ",
    "bat    lortnoc",
    " ",
    "    ",
    "",
    "?noitautcnup",
    "noitautcnup?",
    "tcnup.noitau",
    "?#.@*noitautcnup",
    "noitautcnup!@#$%",
    "?@p noitautcnu.% #itlum",
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset sihT si a tset "
    "sihT si a tset sihT si a tset sihT si a tset sihT si a tset"

};

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of mccarl
mccarl
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of bud6204
bud6204

ASKER

I've requested that this question be deleted for the following reason:

The code I posted in my question contains my name in the comments (I forgot to remove it prior to posting) and I'd like to remove it to remain more anonymous.
You can 'Request Attention' and get the moderators to edit the question for you. Otherwise, open the new question (with edited code) before this one deletes and I can copy/paste my comment over to the new one!
Avatar of bud6204

ASKER

Ok thanks mccarl. I am new to using this site and did not know how to fix it without deleting the entire question. Your solution seems like it will do what I want, but unfortunately, I have to use the functions supplied.  I may be incorrectly interpreting the desired results of the program, which I have inquired about and am waiting on a response. I will try to proceed from there.
> I am new to using this site and did not know how to fix it

No worries. Welcome to EE !


> I have to use the functions supplied

Ah, then this is different. I have been thinking more about the problem and considering this constraint, I ended up with something that in the end is a quite simple change to the above...

1. Comment out (delete) line 117 of tools.c, this will stop the code from discarding important info about punctuation/spaces.

2. Due to 1. above, you now need to change how you locate the end of words, change line 22 of tools.c to the following...

while(!is_separator(*end_ptr)) {

Open in new window


which makes it just look for a separator char to indicate the end of word rather than look for a NULL character

3. Comment out (delete) lines 187-188 of tools.c, as you no longer need to restore NULL's to spaces, due to step 1. above.



> I may be incorrectly interpreting the desired results of the program

Would the desired result just be that the tests pass?
@jkr, looks like there are (at least) 4 more locations in tools.c, where "AUTHOR" needs to be removed.
The delete request can be cancelled now as the identifying information has kindly been removed by jkr
Avatar of bud6204

ASKER

Thanks jkr and mccarl. It looks like you got them all. Much appreciated. Now back to the original question.

I did misinterpret the expected results. I wrote the tests, and assumed they all should pass, but I did not realize that I should've expected some of them to fail based on the implementation of the words_initialize function and the words_modify function. Basically I had the program operating as desired already, but was trying to achieve something that was not possible with the intended design.

It is expected that punctuation may often be lost since it is treated as a separator, and therefore can be replaced by '\0', then eventually by a <space>. But the punctuation is really irrelevant because the only thing we care about are the words in the phrase. As for the trailing spaces, I should also expect them to be truncated because words_modify function is only called for nbwords - 1  --  meaning that for 1 word phrases, or multi-word phrases with trailing separators, the last '\0' never gets changed to a space so it becomes the end of the string and any remaining characters are lost.

But all efforts were not at a loss...I learned a lot and based on mccarl's responses, I was also able to write a similar program (just to see if I could) that does what I thought the original program was supposed to do :)  Now I have two working programs. Thanks!
Not a problem, glad to help. And I'm happy that in the end, you were able to resolve all your issues.