splitting two arrays.

P

Pedro Pinto

Hi there.

I'm trying to do the following.

I have a string, and i want to separate it into other halves.
This is how it should be:

char string[] = "test//test2//test3";

were // is the part were i want to separate it and store on a
bidimensional array like name[50][65],

were 50 diferent names are allowed, each name have 65 bytes of length.

i know how to copy the first bytes until the first //

How can it do to split the string and insert it in name in this onder

name[0] = test
name[1] = test2
name[3] = test3
........
........

thanks in advance for any help.

Regards
 
R

Richard Heathfield

Pedro Pinto said:
Hi there.

I'm trying to do the following.

I have a string, and i want to separate it into other halves.
This is how it should be:

char string[] = "test//test2//test3";

were // is the part were i want to separate it and store on a
bidimensional array like name[50][65],

were 50 diferent names are allowed, each name have 65 bytes of length.

i know how to copy the first bytes until the first //

How can it do to split the string and insert it in name in this onder

name[0] = test
name[1] = test2
name[3] = test3

Look up strchr and memcpy.
 
J

John Gordon

In said:
Hi there.
I'm trying to do the following.
I have a string, and i want to separate it into other halves.
This is how it should be:
char string[] = "test//test2//test3";
were // is the part were i want to separate it and store on a
bidimensional array like name[50][65],
were 50 diferent names are allowed, each name have 65 bytes of length.
i know how to copy the first bytes until the first //
How can it do to split the string and insert it in name in this onder
name[0] = test
name[1] = test2
name[3] = test3

Try strtok(). strtok() is a function that splits a string into pieces,
based on a separation string you provide.

char string[] = "test//test2//test3";
char name[50][65];
char *tmp;
int i = 0;

tmp = strtok(string, "//");
strcpy(name[0], tmp);

while(tmp = strtok(NULL, "//"))
strcpy(name[++i], tmp);
 
W

Walter Roberson

John Gordon said:
Try strtok(). strtok() is a function that splits a string into pieces,
based on a separation string you provide.

Not -exactly-.

char string[] = "test//test2//test3";

Note to the original poster: it is important in this example
that the type is char string[] and not char *string .
In the form that John gave, string[] will be writable; if he
had used char *string = "test//test2//test3"; then string[]
would be read-only, which would cause a problem with strtok() as
strtok() modifies the string itself.
char name[50][65];
char *tmp;
int i = 0;
tmp = strtok(string, "//");

The second argument to strtok() is not a a literal string to match
against (in this example, the character pair // ). The second argument
to strtok() is instead a string, each character of which is to be treated
as a delimiter for tokenization. For example strtok() with ":/" as
the second argument would stop the tokenization at any character
position that was a ':' or a '/'. Specifying "//" is thus the same
as specifying just "/" and each of the '/' encountered would
act as a delimiter. For example, a/b//test2 would break down
into the tokens "a" "b" and "test2"
strcpy(name[0], tmp);

while(tmp = strtok(NULL, "//"))
strcpy(name[++i], tmp);
 
J

John Gordon

The second argument to strtok() is not a a literal string to match
against (in this example, the character pair // ). The second argument
to strtok() is instead a string, each character of which is to be treated

Doh! I've been too long away from C.
 
S

Simon Biber

Richard said:
Pedro Pinto said:
Hi there.

I'm trying to do the following.

I have a string, and i want to separate it into other halves.
This is how it should be:

char string[] = "test//test2//test3";

were // is the part were i want to separate it and store on a
bidimensional array like name[50][65],

were 50 diferent names are allowed, each name have 65 bytes of length.

i know how to copy the first bytes until the first //

How can it do to split the string and insert it in name in this onder

name[0] = test
name[1] = test2
name[3] = test3

I think name[2] = test3, not name[3].
Look up strchr and memcpy.

Perhaps strstr would be more appropriate, given that the delimiter
consists of more than one character.

#include <stdio.h>
#include <string.h>

#define MAX_LEN 64
#define MAX_NAM 50

int main(void)
{
const char *delim = "//";
const char *string = "test//test2//test3";
const char *p = string, *q;
char name[MAX_NAM][MAX_LEN + 1];
int i, n = 0;
do {
if(n == MAX_NAM)
{
fprintf(stderr, "Too many names\n");
return 0;
}
q = strstr(p, delim); /* find next delim */
if(!q) q = strchr(p, '\0'); /* if no delim, end of str */
if(q - p > MAX_LEN)
{
fprintf(stderr, "Name too long\n");
return 0;
}
memcpy(name[n], p, q - p); /* copy contents */
name[n][q - p] = '\0'; /* and null terminate */
n++;
if(*q) p = q + strlen(delim); /* if not end of str, skip delim */
} while(*q); /* repeat until end of string reached */
for(i = 0; i < n; i++)
{
printf("name[%d] = %s\n", i, name);
}
return 0;
}
 
P

Pedro Pinto

John Gordon escreveu:
Doh! I've been too long away from C.

The solucion pointed by John works well but i have one last question!

Before i send my original string to the funtion, it has, lets supose,
16 of length. After the funtion it only has four!

Original string = "test/test1/test2
after use of funtion = "test"


i created this function to do the calculation far from main:

void divide(char search_string[], char *array[]){

int loop;

array[0]=strtok(search_string,"/");
if(array[0]==NULL)
{
printf("No test to search.\n");
exit(0);
}

for(loop=1;loop<50;loop++)
{
array[loop]=strtok(NULL,"/");
if(array[loop]==NULL)
break;
}

for(loop=0;loop<50;loop++)
{
if(array[loop]==NULL)
break;

printf("\narray[%d] = %s", loop,array[loop]);

}
}


thank you for the efford, i hope someday i will be able to help you!

Regards Pedro
 
M

mark_bluemel

Pedro said:
John Gordon escreveu:

The solucion pointed by John works well but i have one last question!

Before i send my original string to the funtion, it has, lets supose,
16 of length. After the funtion it only has four!

Read the manual page for strtok, carefully - one of its (many) foibles
is that it changes the string it is tokenizing.
Original string = "test/test1/test2
after use of funtion = "test"

Because strtok has converted the '/' characters into '\0' as it worked.

If this is a problem, clone your string (on some platforms, a strdup()
function is available for this purpose, otherwise you can use malloc
and strcpy()).

Alternatively you can use one of the strchr() based solutions discussed
elsewhere in the thread.

BTW, I note that your requirement has changed - you now use a single
'/' not a "//" string as a delimiter.

What do you intend to happen with "test/test1//test3" - does the
strtok() solution do what you want?
 
S

Simon Biber

Pedro said:
John Gordon escreveu:

The solucion pointed by John works well but i have one last question!

Before i send my original string to the funtion, it has, lets supose,
16 of length. After the funtion it only has four!

Original string = "test/test1/test2
after use of funtion = "test"

That's one of the issues with strtok. It modifies the original string,
replacing the delimiters with null characters. The pointers it returns
to tokens are in fact pointers within your original string, but they are
smaller strings since they end on the null characters.

If you still want to use strtok, you're best to make a copy of the
string first:

char *duplicate_string(const char *a)
{
char *new = malloc(strlen(a) + 1);
if(!new) return NULL;
strcpy(new, a);
return new;
}

Also keep in mind that strtok considers adjacent delimiters as one.
There's no way to produce an empty token, for example:

"test/test1//test3" -> {"test", "test1", "test3"}
but not {"test", "test1", "", "test3"}
i created this function to do the calculation far from main:

void divide(char search_string[], char *array[]){

int loop;

array[0]=strtok(search_string,"/");
if(array[0]==NULL)
{
printf("No test to search.\n");
exit(0);
}

for(loop=1;loop<50;loop++)
{
array[loop]=strtok(NULL,"/");
if(array[loop]==NULL)
break;
}

for(loop=0;loop<50;loop++)
{
if(array[loop]==NULL)
break;

printf("\narray[%d] = %s", loop,array[loop]);

}
}

Ok, this function is fine, but remember that the pointers that are
placed in your array are actually pointing into the original
search_string[] array.

After the function completes, the search_string[] array contains a
number of strings, one after the other, and pointers to the start of
each of these strings are placed into array[].

No extra memory was allocated to store your tokens; the text is still
sitting where is was in the first place, but the delimiters ('/'
characters) have been replaced by null characters ('\0').
 
D

Default User

Simon said:
That's one of the issues with strtok. It modifies the original
string, replacing the delimiters with null characters. The pointers
it returns to tokens are in fact pointers within your original
string, but they are smaller strings since they end on the null
characters.

Also keep in mind that strtok considers adjacent delimiters as one.
There's no way to produce an empty token, for example:


I was going to refer the OP to an appropriate FAQ section, but
surprisingly there doesn't seem to be one for strtok() and tokenizing
issues in general.

It seems like one of those things that might be useful to have in the
FAQs, but I don't maintain it and don't wish to insist that Steve do
extra work to accomodate that interest.




Brian
 
G

Giorgos Keramidas

That's one of the issues with strtok. It modifies the original string,
replacing the delimiters with null characters. The pointers it returns
to tokens are in fact pointers within your original string, but they are
smaller strings since they end on the null characters.

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):

#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;
vec[0] = NULL;

/*
* 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 + 1;
}

/* 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;
}
 
J

Joe Wright

Giorgos said:
That's one of the issues with strtok. It modifies the original string,
replacing the delimiters with null characters. The pointers it returns
to tokens are in fact pointers within your original string, but they are
smaller strings since they end on the null characters.

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):

#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;
vec[0] = NULL;

/*
* 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 + 1;
}

/* 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;
}

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?

Anyway, a long and complex attempt at a very simple problem.
 
J

Joe Wright

Pedro said:
Hi there.

I'm trying to do the following.

I have a string, and i want to separate it into other halves.
This is how it should be:

char string[] = "test//test2//test3";

were // is the part were i want to separate it and store on a
bidimensional array like name[50][65],

were 50 diferent names are allowed, each name have 65 bytes of length.

i know how to copy the first bytes until the first //

How can it do to split the string and insert it in name in this onder

name[0] = test
name[1] = test2
name[3] = test3
.......
.......

thanks in advance for any help.

Regards
#include <stdio.h>
#include <stdlib.h>

int main(void) {
int i, c, row = 1, col = 65;
char *n, *p, **names;
/* Limit separator to one '/' character */
char s[] = "test0/test3/test4/test6";
/* Count separators (+1) to n */
p = s;
while ((c = *p++)) {
if (c == '/')
++row;
}
printf("In \"%s\" there are %d rows\n", s, row);
names = malloc(row * sizeof *names);
for (i = 0; i < row; ++i)
names = malloc(col);
/* Replace separators in s with 0 */
p = s;
while (*p) {
if (*p == '/')
*p = 0;
++p;
}
/* Now load up the names array */
p = s;
for (i = 0; i < row; ++i) {
n = names;
while ((*n++ = *p++)) ;
}
for (i = 0; i < row; ++i)
printf("%s\n", names);
return 0;
}
 
G

Giorgos Keramidas

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 :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top