Giorgos said:
I usually prefer strstr() and strcspn() for this sort of thing. When
delimiters are single characters, something like the following works
quite well, IMHO (the strcspn() function can also handle `empty fields',
when consecutive occurences of the separator characters are found):
[snip bogus program]
I downloaded the program and ran it on my machine. I got ..
Initial string = "test0//test3/test4//test6"
Field zd = "(null)"
Field zd = ""
Field zd = ""
Field zd = ""
Field zd = ""
Field zd = ""
Field zd = ""
Is this what I should expect? What do you get?
Right. I had a bug in the initial program. When strcspn() moves past the
end of the last part of the initial string, it returns the position of the
terminating '\0' character. I was blindly moving the `p' pointer with:
p = p + pos + 1;
past the end of the initial string. The bug was easy to find once I turned
on malloc() debugging in FreeBSD, so the fixed program is now (modified
lines marked with '#' at their beginning):
| #include <stdio.h>
| #include <stdlib.h>
| #include <string.h>
|
| static const char *s = "test0//test3/test4//test6";
| static const char *sep = "/";
|
| int
| main(void)
| {
| char **vec, **tmp;
| size_t vsize, vlen;
| const char *p;
| size_t j, k, pos;
|
| /*
| * Allocate some initial space for vec[], which we'll grow
| * afterwards as necessary.
| */
| vsize = 1;
| vec = malloc(vsize * sizeof *vec);
| if (vec == NULL)
| goto err;
#| memset(vec, 0, (vsize * sizeof *vec));
|
| /*
| * Split the string from `s' to a newly allocated vector of
| * strings, stored in vec[]. The allocated size of vec[] is
| * kept in `vsize' and the in-use part of vec[] is vec[0]
| * ... vec[vlen-1] (a total of `vlen' elements).
| */
| for (p = s, k = vlen = 0; p != NULL && *p != 0; k++) {
| /* Find the position of the next separator. */
| pos = strcspn(p, sep);
|
| /*
| * Make sure vec[] can fit another element.
| */
| if (k >= vsize) {
| tmp = realloc(vec, (2 * vsize) * sizeof *vec);
| if (tmp == NULL)
| goto err;
| vec = tmp;
| vsize *= 2;
| for (j = vlen; j < vsize; j++)
| vec[j] = NULL;
| }
| vec[k] = malloc((pos + 1) * sizeof(char));
| if (vec[k] == NULL)
| goto err;
| memset(vec[k], 0, pos + 1);
| strncpy(vec[k], p, pos);
| vlen++;
#| p = p + pos;
#| if (*p != '\0')
#| p++;
| }
|
| /* Print the resulting string vector. */
| (void)printf("Initial string = \"%s\"\n", s);
| for (k = 0; k < vlen; k++)
| (void)printf(" Field %3zd = \"%s\"\n", k, vec[k]);
|
| /* Clean up and exit. */
| for (k = 0; k < vsize; k++) {
| free(vec[k]);
| vec[k] = NULL;
| }
| free(vec);
| vec = NULL;
| return EXIT_SUCCESS;
|
| /*
| * Clean up and print a warning that some memory allocation
| * has failed (this may not work 100% correctly, because
| * fprintf() may also fail to work on low-memory conditions :-/
| */
| err:
| if (vec != NULL) {
| for (k = 0; k < vsize; k++) {
| free(vec[k]);
| vec[k] = NULL;
| }
| free(vec);
| vec = NULL;
| }
| (void)fprintf(stderr, "Out of memory.\n");
| return EXIT_FAILURE;
| }
Anyway, a long and complex attempt at a very simple problem.
This is something I am frequently accused of. Then again, I like
covering as many edge cases as possible, as early as possible
If you actually remove all the house-keeping around malloc() and the parts
which make sure that there is an adequately sized vec[] array of pointers
to the string-parts, then this is (IMHO) a very `simple' wrapper around
strcspn() and strncpy().
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Heh! Nice signature
