is there an easier way?

Giorgos Keramidas keramida at ceid.upatras.gr
Tue Feb 19 00:45:09 UTC 2008


On 2008-02-18 15:03, Gary Kline <kline at thought.org> wrote:
>
>       To my fellow C nerds,
>
>       It's been a  great manny years since I wrote this appended
>       snippet.  Now I can't remember why (of if ) I need all the
>       strcpy() calls.   Is there a simpler, more logical way of
>       printing a bunch of string by snipping off the left-most?
>
>       In short,, can anyone 'splain why strtok needs all this?

> #include <stdio.h>
> #include <string.h>
>
> main()
> {
>   char *bp, buf[512], *tok, tstr[512];
>   static char *delim=" ", s1[256]="abc def ghi jkl mno.";
>
>   bp = strcpy(buf, tstr);
>   strcpy(bp, s1); /* bp filled with writable mem works like this, too */
>   while ((tok = strtok(bp, delim)) != NULL)
>   {
>     bp = NULL;
>     printf("tok = [%s]\n", tok);
>   }
> }

I don't know about `simpler', but the hardcoded `magic' numbers above,
like 256 and 512, make me nervous.

Another point which is worth noting is that you don't need to copy the
string at all, for one thing.

If you have a string defined like this:

        const char *s = "hello world";

Then you can use a second pointer to point somewhere within that string
and print from that point:

        const char *point = s + 7;
        printf("%s\n", point);

So if you want to strip the first 'word' from a C string, you can do
this in the following steps:

  [0] Get the initial string in your s1[] buffer, i.e.:

          const char s1[] = "abc def ghi jkl mno.";

      Note that if you use `s1[]' to define the string, the
      compiler is smart enough to grab enough space for it.  This
      way you don't need all the `magic' numbers, like 256 and
      512 in the original code.

  [1] Find the first whitespace character in the string, using
      strcspn() to match everything *except* a whitespace:

          const char *p;
          size_t num_chars;

          num_chars = strcspn(s1, " \t");
          p = s1 + num_chars;

  [2] If the character pointed at by the new value of `p' is the
      terminating '\0' character of the original string, you are
      done; the original string contains a single word, so you
      can skip it entirely.

          if (*p == '\0') {
              printf("\n");
              return;
          }

  [3] Now you can use strspn(), which is the *inverse* operation,
      to skip the whitespace characters you just found:

          num_chars = strspn(p, " \t");
          p += num_chars;

  [4] If the character pointed at by the new value of `p' is the
      terminating '\0' character of the original string, then you
      hit the end of the string again, so there is no `second'
      word; you can move on again:

          if (*p == '\0') {
              printf("\n");
              return;
          }

  [5] If you are still around, then `p' now points right at the
      beginning of the second word.

          printf("%s\n", p);
          return;

A sample function which returns a pointer to the second word of the
string you pass to it, could be something like this:

    #include <stdlib.h>
    #include <string.h>

    #define SPACECHARS      " \t"

    char *
    secondword(char *s)
    {
        size_t num_chars;
        char *p;

        if (s == NULL)
            return NULL;

        /*
         * Skip any non-whitespace chars first.
         */
        num_chars = strcspn(s, SPACECHARS);
        p = s + num_chars;
        if (*p == '\0')
            return p;

        /*
         * Then skip whitespace, until we hit the end of the
         * string, or we find the second word.
         */
        num_chars = strspn(p, SPACECHARS);
        p += num_chars;

        /*
         * Even if we just hit '\0' or a non-space, we can
         * simply return whatever our next match character is.
         * It's either going to end up being the start of an
         * empty string, or the second word we wanted to find.
         */
        return p;
    }



More information about the freebsd-questions mailing list