Reversing a string w strtok() & sprintf()
Author |
Message |
Amittai Avira #1 / 20
|
 Reversing a string w strtok() & sprintf()
The following code will give you a mere (lame) copy of the string you enter on the command line. I had thought that, if I reversed the order of the parameters "copy" and "place" in the call to sprintf(), I could cause the string to be assembled backwards in copy[] and thus to be printed. But, instead, I get weird behavior -- repetition of the last line of the string. Why? /* reverser.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char copy[strlen(argv[1]) + 1]; char *place = NULL; copy[0] = '\0'; if (argc != 2) { puts("Please enter only one string enclosed in quotation marks."); exit(EXIT_FAILURE); } printf("Original string: %s\n", argv[1]); for (place = strtok(argv[1], " "); place; place = strtok(NULL, " ")) sprintf(copy, "%s %s", copy, place); /* Why not place, copy? */ printf("Copy: %s\n", copy); return EXIT_SUCCESS; Quote: }
Suppose the two parameters "place" and "copy" were reversed. Now suppose I entered "This is a string." On the first iteration, before sprintf(), copy should contain garbage and place should point to "This\0". So sprintf() should cause copy to contain " This\0". On the next iteration, place should point to "is\0" and copy, still, to " This\0", so sprintf() should cause copy to contain "is This\0" -- and so forth. Why doesn't this work? Instead, the ouput for input "This is a string." is somefhing like this: string. string. string. string. I know I should understand why I get this output, but, if I did, I wouldn't be posting. :-( Thanks! Amittai Aviram
|
Sun, 27 Jun 2004 09:35:55 GMT |
|
 |
Amittai Avira #2 / 20
|
 Reversing a string w strtok() & sprintf()
Never mind. -- see comments inside & below. --AA
Quote: > The following code will give you a mere (lame) copy of the string you enter > on the command line. I had thought that, if I reversed the order of the > parameters "copy" and "place" in the call to sprintf(), I could cause the > string to be assembled backwards in copy[] and thus to be printed. But, > instead, I get weird behavior -- repetition of the last line of the
string. Sorry! I meant "repetition of the last token of the string." Quote: > Why? > /* reverser.c */ > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > int main(int argc, char *argv[]) > { > char copy[strlen(argv[1]) + 1]; > char *place = NULL; > copy[0] = '\0'; > if (argc != 2) > { > puts("Please enter only one string enclosed in quotation marks."); > exit(EXIT_FAILURE); > } > printf("Original string: %s\n", argv[1]); > for (place = strtok(argv[1], " "); > place; > place = strtok(NULL, " ")) > sprintf(copy, "%s %s", copy, place); > /* Why not place, copy? */ > printf("Copy: %s\n", copy); > return EXIT_SUCCESS; > } > Suppose the two parameters "place" and "copy" were reversed. Now suppose I > entered "This is a string." On the first iteration, before sprintf(), copy > should contain garbage and place should point to "This\0". So sprintf() > should cause copy to contain " This\0". On the next iteration, place should > point to "is\0" and copy, still, to " This\0", so sprintf() should cause > copy to contain "is This\0" -- and so forth. Why doesn't this work? > Instead, the ouput for input "This is a string." is somefhing like this: > string. string. string. string. > I know I should understand why I get this output, but, if I did, I wouldn't > be posting. :-( Thanks! > Amittai Aviram
O.k., I think I see what's happening. I am writing copy onto itself. I would have to save copy somewhere else first and then write _that_ as the second of the two strings to be written onto copy. Of course, that defeats one of my purposes, which was to use as little memory for this exercise as possible. Anyway, I tried making a second array, copy2, and copying copy to copy2 before calling sprintf, with place and copy2 as my two parameters after the conversion specifiers, and that works. Thanks anyway! Amittai
|
Sun, 27 Jun 2004 10:45:07 GMT |
|
 |
Amittai Avira #3 / 20
|
 Reversing a string w strtok() & sprintf()
Here is my solution to the problem I posed on this thread. Forgive me for this dialogue with myself ... :-) Naturally, I would be grateful for any comments you sages might care to offer about this version. As always, thanks! Amittai Aviram /* reverser.c Reverses the order of tokens (words) in a string entered on the command line */ #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char reversed[strlen(argv[1]) + 1]; char *place = NULL; char *revplace = NULL; memset(reversed, ' ', sizeof(reversed)); reversed[sizeof(reversed)] = '\0'; if (argc != 2) { puts("Please enter only one string enclosed in quotation marks."); exit(EXIT_FAILURE); } printf("Original string: %s\n", argv[1]); revplace = &reversed[sizeof(reversed)]; for (place = strtok(argv[1], " "); place; place = strtok(NULL, " ")) { revplace = revplace - strlen(place) - 1; strncpy(revplace, place, strlen(place)); } printf("Reversed: %s\n", reversed); return EXIT_SUCCESS; Quote: }
|
Sun, 27 Jun 2004 11:41:46 GMT |
|
 |
David Rubi #4 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
> /* reverser.c Reverses the order of tokens (words) > in a string entered on the command line */ > #include <stdio.h> > #include <stdlib.h> > #include <string.h> > int main(int argc, char *argv[]) > { > char reversed[strlen(argv[1]) + 1];
This will fail if argc < 2 which you check for later on (as argc != 2). Quote: > char *place = NULL; > char *revplace = NULL; > memset(reversed, ' ', sizeof(reversed)); > reversed[sizeof(reversed)] = '\0';
There's a nice trick you can do with sprintf here. Something like sprintf(reversed, "%*s", sizeof reversed - 1, " "); Quote: > if (argc != 2) > { > puts("Please enter only one string enclosed in quotation marks.");
This is shell-specific: in my shell, I have to use single-quotes. Quote: > exit(EXIT_FAILURE); > } > printf("Original string: %s\n", argv[1]); > revplace = &reversed[sizeof(reversed)]; > for (place = strtok(argv[1], " "); > place; > place = strtok(NULL, " "))
Note that strtok replaces some characters in argv[1] with NUL. IIRC, argv strings may be read-only. Place is sort of an unconventional name for a string or token IMO. Quote: > { > revplace = revplace - strlen(place) - 1; > strncpy(revplace, place, strlen(place));
I don't see any reason to use strncpy over strcpy since you already allocated enough space. Quote: > } > printf("Reversed: %s\n", reversed); > return EXIT_SUCCESS; > }
A somewhat nicer way to do this, and less complicated, would be to reverse each token as you write it to revplace (in-order rather than front-to-back), and then reverse revplace before printing it. david -- If 91 were prime, it would be a counterexample to your conjecture. -- Bruce Wheeler
|
Sun, 27 Jun 2004 09:28:35 GMT |
|
 |
Russ Bobbi #5 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
>> printf("Original string: %s\n", argv[1]); >> revplace = &reversed[sizeof(reversed)]; >> for (place = strtok(argv[1], " "); >> place; >> place = strtok(NULL, " ")) >Note that strtok replaces some characters in argv[1] with NUL. IIRC, argv >strings may be read-only.
Actually, the standard guarantees that they're *not* read-only. From 5.1.2.2.1(2) of C99: The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination. Russ
|
Sun, 27 Jun 2004 13:39:25 GMT |
|
 |
Amittai Avira #6 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
> > /* reverser.c Reverses the order of tokens (words) > > in a string entered on the command line */ > > #include <stdio.h> > > #include <stdlib.h> > > #include <string.h> > > int main(int argc, char *argv[]) > > { > > char reversed[strlen(argv[1]) + 1]; > This will fail if argc < 2 which you check for later on (as argc != 2).
Yes, I can see that, and I can see that I cannot do anything else so long as I must use an array. So by far the better way would be to use malloc() to make the copy, as I had done in my earlier version of this exercise (months ago). That would make it compatible with pre-'99 ANSI/ISO C, too. Quote: > > char *place = NULL; > > char *revplace = NULL; > > memset(reversed, ' ', sizeof(reversed)); > > reversed[sizeof(reversed)] = '\0'; > There's a nice trick you can do with sprintf here. Something like > sprintf(reversed, "%*s", sizeof reversed - 1, " ");
Thanks. So the point is that this combines the two steps in one, because sprintf will always close the end off with '\0'? Quote: > > if (argc != 2) > > { > > puts("Please enter only one string enclosed in quotation marks."); > This is shell-specific: in my shell, I have to use single-quotes.
Aha. Will change the text to something vaguer and more portable. :-) Quote: > > exit(EXIT_FAILURE); > > } > > printf("Original string: %s\n", argv[1]); > > revplace = &reversed[sizeof(reversed)]; > > for (place = strtok(argv[1], " "); > > place; > > place = strtok(NULL, " ")) > Note that strtok replaces some characters in argv[1] with NUL. IIRC, argv > strings may be read-only. Place is sort of an unconventional name for a string > or token IMO.
Yeah. I am going to rewrite the whole thing again so as to treat the input as a constant, so I cannot use strtok() at all, so long as I want to minimize memory use -- which would require that I strcpy() the whole thing -- and then copy it _again_ in the process of reversing it -- unless I resorted to a laborious process of shifting the characters one by one to the right place using a single-character buffer -- which would defeat the purpose of trying to make a neat little program that benefits from the string.h functions. Quote: > > { > > revplace = revplace - strlen(place) - 1; > > strncpy(revplace, place, strlen(place)); > I don't see any reason to use strncpy over strcpy since you already allocated > enough space.
Oh! Because strcpy() adds the terminal '\0'. At least, that's what I just read -- I checked it -- and then I tested it myself to make sure. That's specifically why I used strncpy() instead. It does _not_ add the terminal '\0', as part of the logic of copying a specified number of characters. Quote: > > } > > printf("Reversed: %s\n", reversed); > > return EXIT_SUCCESS; > > } > A somewhat nicer way to do this, and less complicated, would be to reverse each > token as you write it to revplace (in-order rather than front-to-back), and then > reverse revplace before printing it.
I'm sorry -- I do not follow. The other way -- I am about to do in another version -- is to start at the end of the input string and use strrchr() to find the first space, then copy that to the beginning of the reverse string, etc. Tomorrow ... Amittai
|
Sun, 27 Jun 2004 14:29:46 GMT |
|
 |
Amittai Avira #7 / 20
|
 Reversing a string w strtok() & sprintf()
Quote: > Actually, the standard guarantees that they're *not* read-only. > From 5.1.2.2.1(2) of C99: > The parameters argc and argv and the strings pointed to by the argv > array shall be modifiable by the program, and retain their last-stored > values between program startup and program termination. > Russ
This is very good to know. Thanks. Amittai
|
Sun, 27 Jun 2004 14:30:58 GMT |
|
 |
Ra #8 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
> int main(int argc, char *argv[]) > { > char reversed[strlen(argv[1]) + 1];
Are you sure the above line compiled??? Later Raj
|
Sun, 27 Jun 2004 15:58:28 GMT |
|
 |
Ben Pfaf #9 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
> > int main(int argc, char *argv[]) > > { > > char reversed[strlen(argv[1]) + 1]; > Are you sure the above line compiled???
It's fine in C99. The OP might be using a C90 compiler with a VLA-like extension. GCC is an example of such a compiler.
|
Sun, 27 Jun 2004 16:00:11 GMT |
|
 |
Peter Nilsso #10 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
> > /* reverser.c Reverses the order of tokens (words) > > in a string entered on the command line */ > > #include <stdio.h> > > #include <stdlib.h> > > #include <string.h> > > int main(int argc, char *argv[]) > > { > > char reversed[strlen(argv[1]) + 1]; > [snip] > > memset(reversed, ' ', sizeof(reversed)); > > reversed[sizeof(reversed)] = '\0'; > There's a nice trick you can do with sprintf here. Something like > sprintf(reversed, "%*s", sizeof reversed - 1, " ");
This invokes UB if *argv[1] == 0. So perhaps not something _exactly_ like it. :) Equally ugly would be... sprintf(reversed, "%*.*s", sizeof reversed - 1, sizeof reversed - 1, " " ); -- Peter
|
Sun, 27 Jun 2004 17:53:01 GMT |
|
 |
David Rubi #11 / 20
|
 Reversing a string w strtok() & sprintf()
Quote:
> > A somewhat nicer way to do this, and less complicated, would be to reverse > > each token as you write it to revplace (in-order rather than > > front-to-back), and then reverse revplace before printing it. > I'm sorry -- I do not follow.
For example, if s="hello world", and r is the result string, r becomes (step-by-step): r="olleh " r="olleh dlrow " r="world hello" This way you can parse s front-to-back and use the reverse function for each token (word) and then for the whole string r itself. Here is my implementation, although Russ Bobbitt corrected my recolection of the writability of argv[], so you could dispense with s and dupstr below if you wanted. #include <stdio.h> #include <stdlib.h> #include <string.h> char * dupstr(char *s) { char *r; r = malloc(strlen(s)+1); strcpy(r, s); return r; Quote: }
char * reverse(char *buf, int off, int len) { char *s, *e, c; for(s=buf+off, e=buf+len; e > s; e--, s++){ c = *s; *s = *e; *e = c; } return buf; Quote: }
int main(int argc, char *argv[]) { char *s, *t, *r; s = dupstr(argv[1]); /* common rewrite of strdup */ r = malloc(strlen(s)+2); /* reversed argv[1] plus ' ' */ for(t=strtok(s, " "); t != 0; t=strtok(0, " ")){ strcat(r, reverse(t, 0, strlen(t)-1)); strcat(r, " "); } r[strlen(r)] = 0; /* get rid of last ' ' */ printf("s=%s\nr=%s\n", argv[1], reverse(r, 0, strlen(r)-1)); return 0; Quote: } > The other way -- I am about to do in another version -- is to start at the > end of the input string and use strrchr() to find the first space, then copy > that to the beginning of the reverse string, etc. Tomorrow ...
This would work all right, except there is a special case: when strrchr returns NULL you have to access the last token by the string pointer (argv[1]?) rather than "pointer-plus-one." david -- If 91 were prime, it would be a counterexample to your conjecture. -- Bruce Wheeler
|
Mon, 28 Jun 2004 08:22:56 GMT |
|
 |
David Rubi #12 / 20
|
 Reversing a string w strtok() & sprintf()
Quote: [snip] Quote: > > > int main(int argc, char *argv[]) > > > { > > > char reversed[strlen(argv[1]) + 1]; > > [snip] > > > memset(reversed, ' ', sizeof(reversed)); > > > reversed[sizeof(reversed)] = '\0'; > > There's a nice trick you can do with sprintf here. Something like > > sprintf(reversed, "%*s", sizeof reversed - 1, " "); > This invokes UB if *argv[1] == 0. So perhaps not something _exactly_ like > it. :)
Well, you snipped the part of my message where I said you have to check argv[1] :-) david -- If 91 were prime, it would be a counterexample to your conjecture. -- Bruce Wheeler
|
Mon, 28 Jun 2004 08:25:06 GMT |
|
 |
David Rubi #13 / 20
|
 Reversing a string w strtok() & sprintf()
[snip] Quote: > for(t=strtok(s, " "); t != 0; t=strtok(0, " ")){ > strcat(r, reverse(t, 0, strlen(t)-1)); > strcat(r, " "); > } > r[strlen(r)] = 0; /* get rid of last ' ' */
r[strlen(r)-1] = 0; david -- If 91 were prime, it would be a counterexample to your conjecture. -- Bruce Wheeler
|
Mon, 28 Jun 2004 08:47:22 GMT |
|
 |
Ra #14 / 20
|
 Reversing a string w strtok() & sprintf()
Quote: > It's fine in C99. The OP might be using a C90 compiler with a > VLA-like extension. GCC is an example of such a compiler.
Thanks for the insight Ben, but can you explain it a bit further (simple arrays allocated dynamically or something???). Thanks Raj
|
Mon, 28 Jun 2004 13:04:01 GMT |
|
 |
David Rubi #15 / 20
|
 Reversing a string w strtok() & sprintf()
[snip] Quote: > r = malloc(strlen(s)+2); /* reversed argv[1] plus ' ' */
Oops! It would be nice if I initialized r before I started strcating to it :-) r[0] = 0; Quote: > for(t=strtok(s, " "); t != 0; t=strtok(0, " ")){ > strcat(r, reverse(t, 0, strlen(t)-1)); > strcat(r, " "); > }
david -- If 91 were prime, it would be a counterexample to your conjecture. -- Bruce Wheeler
|
Tue, 29 Jun 2004 00:14:51 GMT |
|
|
Page 1 of 2
|
[ 20 post ] |
|
Go to page:
[1]
[2] |
|