Reputation: 23
How can I reverse the words of string but only characters and numbers. For example:
this is the start!
siht si eht trats!
I've written this code but it reverses everything (also the '!'); the output I get is:
siht si eht !trats
Code:
#include<stdio.h>
void reverse(char *begin, char *end);
void reverseWords(char *s)
{
char *word_begin = s;
char *temp = s;
while(isalnum(*temp) || *temp )
{
temp++;
if (*temp == '\0')
{
reverse(word_begin, temp-1);
}
else if(*temp == ' ')
{
reverse(word_begin, temp-1);
word_begin = temp+1;
}
}
}
void reverse(char *begin, char *end)
{
char temp;
while (begin < end)
{
temp = *begin;
*begin++ = *end;
*end-- = temp;
}
}
int main()
{
char s[50];
scanf("%[^\n]",&s);
char *temp = s;
reverseWords(s);
printf("%s", s);
getchar();
return 0;
}
Upvotes: 0
Views: 198
Reputation: 45
I would use the ASCII table and an if
function to say:
'only reverse/move the last element (letter) of the array (word) if
the ASCII character is between 65-90(capitals), 97-122(lower case), and 48-57(decimals)'.
It might make it more tricky if you were to include symbols within the word but I'm assuming you wouldn't as your example would never have an '!' in the middle of a word.
Example:
if ((int) s[i] == 33)
[case specifically where the character in the array s
, position i
happens to be an ! (which is represented by the decimal 33 in the ASCII table.)]
Upvotes: 0
Reputation: 753615
The reverse()
function you have is a useful tool which you need. Many traditional versions rely on passing just the start of the text to be reversed, but for your purposes, passing pointers to the first and last characters of the range to be reversed is very neat. I use it unchanged except for trivia. I make it static
because I make every function (except main()
) static
unless there's a header that declares it and it is used by more than one source file — there's only one source file for this code.
All you have to do then is identify the start and end of each word in the text. This is usually best done with nested loops — one on 'not reached end of input string', and then within that, skip over non-alphabetic (non-word) characters, record the start of the word, skip over alphabetic (word) characters, reverse the word between start and end, and repeat the outer loop.
That leads to code like this.
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static void reverse(char *begin, char *end)
{
while (begin < end)
{
char temp = *begin;
*begin++ = *end;
*end-- = temp;
}
}
static void reverseWords(char *s)
{
while (*s != '\0')
{
while (*s != '\0' && !isalnum((unsigned char)*s))
s++;
char *word = s;
while (isalnum((unsigned char)*s))
s++;
reverse(word, s-1);
}
}
int main(void)
{
char s[1024];
while (fgets(s, sizeof(s), stdin) != 0)
{
s[strcspn(s, "\n")] = '\0';
printf("In: [%s]\n", s);
reverseWords(s);
printf("Out: [%s]\n", s);
}
return 0;
}
The (unsigned char)
cast is necessary to correctly handle accented characters in a single-byte code set such as ISO 8859-15 on a machine where plain char
is a signed type.
Sample output:
this is the start!
In: [this is the start!]
Out: [siht si eht trats!]
and this isn't the end.
In: [and this isn't the end.]
Out: [dna siht nsi't eht dne.]
Neither leading blanks nor trailing ones interfere
In: [ Neither leading blanks nor trailing ones interfere ]
Out: [ rehtieN gnidael sknalb ron gniliart seno erefretni ]
[email protected]^give&no&&&trouble
In: [ [email protected]^give&no&&&trouble ]
Out: [ [email protected]^evig&on&&&elbuort ]
Note that if you decide that apostrophes should be counted as part of the word (so "doesn't
" maps to "t'nseod"
rather than "nseod't"
as in the code above), you just need to fix the condition in the loops. At that point, I'd probably create a static inline
function to do the job:
static inline int is_word_char(int c)
{
return isalpha(c) || c == '\'';
}
Note that <ctype.h>
reserves words starting with is
(and to
) followed by a lower-case letter; using the name is_word_char()
avoids such reserved words. Also, the function is defined to take an int
, just like the functions in <ctype.h>
, for simple consistency.
The reverse words function becomes:
static void reverseWords(char *s)
{
while (*s != '\0')
{
while (*s != '\0' && !is_word_char((unsigned char)*s))
s++;
char *word = s;
while (is_word_char((unsigned char)*s))
s++;
reverse(word, s-1);
}
}
It would even be possible to generalize reverseWords()
by passing a suitable pointer-to-function:
static void reverseWords(char *s, int (*is_word)(int))
{
while (*s != '\0')
{
while (*s != '\0' && !is_word((unsigned char)*s))
s++;
char *word = s;
while (is_word((unsigned char)*s))
s++;
reverse(word, s-1);
}
}
Now you'd remove the inline
from is_word_char()
because you need an actual function for the function pointer, and then you can use:
reverseWords(s, isalnum);
reverseWords(s, is_word_char);
The downside of this degree of generalization is the cost of an actual function call to classify each character, losing the possibilities of inlining the classifier function.
I typed up a preliminary version of this answer with the reverseWords()
function looking like this:
static void reverseWords(char *s)
{
while (*s != '\0')
{
while (!isalnum((unsigned char)*s))
s++;
char *word = s;
while (isalnum((unsigned char)*s))
s++;
reverse(word, s-1);
}
}
The difference is in the !isalnum
loop — there isn't a check for a null byte there. Then I realized that the code must be skipping past the null byte(s). Instrumenting it like this:
static void reverseWords(char *s)
{
while (*s != '\0')
{
while (/**s != '\0' &&*/ !isalnum((unsigned char)*s))
{
printf("= %d", *s);
s++;
}
putchar('\n');
char *word = s;
while (isalnum((unsigned char)*s))
s++;
reverse(word, s-1);
}
}
and then running it on "this is the start!" produced far too much output:
this is the start!
In: [this is the start!]
= 32
= 32
= 32
= 33= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0= -49= 0= -24
= -48= 41= 96
= -122= 46
= -1= 127= 0= 0= -120= -124= 46
= -1= 127= 0= 0
…many lines omitted…
= -1= 127= 0= 0= -118= -113= 46
= -1= 127= 0= 0= -65= -113= 46
= -1= 127= 0= 0= 0= 0= 0= 0= 0= 0= 0= 0
= 95
= 61= 46= 47
Out: [siht si eht trats!]
The output appeared sane — but the code was poking around all sorts of places it shouldn't have been.
The revised code is safe as long as the null byte fails the "is word character" test.
Upvotes: 1