C strtok () divide la cadena en tokens pero mantiene inalterados los datos antiguos

Tengo el siguiente código:

#include  #include  int main (void) { char str[] = "John|Doe|Melbourne|6270|AU"; char fname[32], lname[32], city[32], zip[32], country[32]; char *oldstr = str; strcpy(fname, strtok(str, "|")); strcpy(lname, strtok(NULL, "|")); strcpy(city, strtok(NULL, "|")); strcpy(zip, strtok(NULL, "|")); strcpy(country, strtok(NULL, "|")); printf("Firstname: %s\n", fname); printf("Lastname: %s\n", lname); printf("City: %s\n", city); printf("Zip: %s\n", zip); printf("Country: %s\n", country); printf("STR: %s\n", str); printf("OLDSTR: %s\n", oldstr); return 0; } 

Salida de ejecución:

 $ ./str Firstname: John Lastname: Doe City: Melbourne Zip: 6270 Country: AU STR: John OLDSTR: John 

¿Por qué no puedo mantener los datos antiguos ni en la str ni en el oldstr ¿Qué estoy haciendo mal y cómo no puedo alterar los datos o guardarlos?

cuando haces strtok(NULL, "|") strtock encuentra el token y coloca nulo en su lugar ( reemplaza el token con \0 ) y modifica la cadena.

tu str , se convierte en

 char str[] = John0Doe0Melbourne062700AU; Str array in memory +------------------------------------------------------------------------------------------------+ |'J'|'o'|'h'|'n'|0|'D'|'o'|'e'|0|'M'|'e'|'l'|'b'|'o'|'u'|'r'|'n'|'e'|0|'6'|'2'|'7'|'0'|0|'A'|'U'|0| +------------------------------------------------------------------------------------------------+ ^ replace | with \0 (ASCII value is 0) 

Considerar que el diagtwig es importante porque el carácter '0' y 0 son difidentes (en la cadena 6270 están caracterizados por caracteres con paréntesis entre ' donde para \0 0 es el número)

cuando imprime str usando %s , imprime caracteres hasta la primera \0 que es John

Para mantener su str original sin cambios, debe pujar copy str en alguna variable tempstr y luego usar esa cadena tempstr en strtok() :

 char str[] = "John|Doe|Melbourne|6270|AU"; char* tempstr = calloc(strlen(str)+1, sizeof(char)); strcpy(tempstr, str); 

Ahora use esta cadena tempstr en lugar de str en su código.

Debido a que oldstr es solo un puntero, una asignación no hará una nueva copia de su cadena.

Cópialo antes de pasar str al strtok :

  char *oldstr=malloc(sizeof(str)); strcpy(oldstr,str); 

Su versión corregida:

 #include  #include  #include int main (void) { char str[] = "John|Doe|Melbourne|6270|AU"; char fname[32], lname[32], city[32], zip[32], country[32]; char *oldstr = malloc(sizeof(str)); strcpy(oldstr,str); ................... free(oldstr); return 0; } 

EDITAR:

Como mencionó @CodeClown, en su caso, es mejor usar strncpy . Y en lugar de fijar de fname los tamaños de fname etc., puede tener punteros en su lugar y asignar la memoria según sea necesario, ni más ni menos. De esa manera puedes evitar escribir en el búfer fuera de límites …

Otra idea: sería asignar el resultado de strtok a los punteros *fname , *lname , etc. en lugar de matrices. Parece que el strtok está diseñado para ser utilizado de esa manera después de ver la respuesta aceptada.

Precaución: de esta manera, si cambia la str adicional, eso se reflejaría en fname , lname también. Porque, solo apuntan a datos de str pero no a nuevos bloques de memoria. Entonces, usa oldstr para otras manipulaciones.

 #include  #include  #include int main (void) { char str[] = "John|Doe|Melbourne|6270|AU"; char *fname, *lname, *city, *zip, *country; char *oldstr = malloc(sizeof(str)); strcpy(oldstr,str); fname=strtok(str,"|"); lname=strtok(NULL,"|"); city=strtok(NULL, "|"); zip=strtok(NULL, "|"); country=strtok(NULL, "|"); printf("Firstname: %s\n", fname); printf("Lastname: %s\n", lname); printf("City: %s\n", city); printf("Zip: %s\n", zip); printf("Country: %s\n", country); printf("STR: %s\n", str); printf("OLDSTR: %s\n", oldstr); free(oldstr); return 0; } 

strtok requiere una cadena de entrada grabable y modifica la cadena de entrada. Si desea mantener la cadena de entrada, primero debe hacer una copia de ella.

Por ejemplo:

 char str[] = "John|Doe|Melbourne|6270|AU"; char oldstr[32]; strcpy(oldstr, str); // Use strncpy if you don't know // the size of str 

Simplemente copie el puntero a la cadena, pero no la cadena en sí. Use strncpy() para crear una copia.

 char *oldstr = str; // just copy of the address not the string itself!