Reversing a string w strtok() & sprintf() 
Author Message
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  
 
 [ 20 post ]  Go to page: [1] [2]

 Relevant Pages 

1. Strtok: scanning and converting a string.

2. using strtok to separate strings

3. strtok() chops of string

4. Help: Reading Input Files using fget & strtok

5. using strtok on a delimited string

6. Strtok: scanning and converting a string.

7. Problem with strtok or pointer to the string

8. how to use strtok() to handle two seperate string at same time

9. strtok() chops of string

10. strtok with recursion and other string problems.

11. using strtok() to strip chars from string

12. Q: strtok & gcc

 

 
Powered by phpBB® Forum Software