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
TOOLS.H
TOOLS.C
TESTS.C
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 */
}
}
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
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
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"
};
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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!
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...
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?
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)) {
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
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!
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.
ASKER
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.