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

5 minutos de lectura

avatar de usuario
bsteo

Tengo el siguiente código:

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

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 el str o oldstr¿qué estoy haciendo mal y cómo no puedo alterar los datos o conservarlos?

  • xtmtrx aquí en mi respuesta escribí un código que muestra cómo strtok() funciona (modifica la cadena en el mismo espacio de direcciones), creo que deberías echarle un vistazo:

    –Grijesh Chauhan

    14 de junio de 2013 a las 9:39


  • Espero que uno lea el código fuente de la función strtok() o lea la documentación de la función antes de hacer esa pregunta.

    – PP.

    14 de junio de 2013 a las 9:46


  • Así que aquí está el código fuente de strkok()

    –Grijesh Chauhan

    14 de junio de 2013 a las 9:48

avatar de usuario
grijesh chauhan

Cuando tu lo hagas strtok(NULL, "|") strtok() encontrar token y poner null en lugar (reemplazar token con \0) y modifique la cadena.

usted strse 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)

Considere que el diagrama es importante porque char '0' y 0 son tímidos (en la cadena 6270 son char en la figura entre paréntesis por ' para donde \0 0 es como número)

cuando imprimes str usando %s imprime caracteres hasta el primero \0 es decir John

Para mantener su str original sin cambios, debe copiar primero str en alguna variable tempstr y luego usar eso tempstr cadena en strtok():

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

Ahora usa esto tempstr cadena en lugar de str en su código.

  • Una respuesta bien compilada +1 🙂

    – 0decimal0

    9 de julio de 2013 a las 15:17

avatar de usuario
Pantera Rosa

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

Cópielo antes de pasar str al strtok:

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

Su versión corregida:

#include <stdio.h>
#include <string.h>
#include<malloc.h>
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 los tamaños de fname etc. de antemano, puede tener punteros en su lugar y asignar la memoria según sea necesario, ni más ni menos. De esa manera, puede evitar escribir en el búfer fuera de los límites …

Otra Idea: sería asignar el resultado de strtok a punteros *fname, *lname, etc. en lugar de arreglos. parece que strtok está diseñado para usarse de esa manera después de ver la respuesta aceptada.

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

#include <stdio.h>
#include <string.h>
#include<malloc.h>
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;
}

  • ¡Buena respuesta! tal vez usar strncpy() y no use búferes estáticos ya que algunas personas o ciudades tienen nombres de más de 32 caracteres. Su próxima pregunta será sobre la corrupción de la pila 🙂

    – aggsol

    14 de junio de 2013 a las 9:17

  • En un programa real, en lugar de simplemente algo atado para hacer una pregunta, no olvide free la memoria utilizada por oldstr cuando hayas terminado.

    – nurdglaw

    14 de junio de 2013 a las 9:20

  • @Code Clown, sí, corrupción de pila: P resuelto con la respuesta de Grijesh Chauhan

    – bsteo

    14 de junio de 2013 a las 9:28

  • free(str) y free(oldstr)

    – bsteo

    14 de junio de 2013 a las 9:29

  • @pinkpanther aquí no está, pero en el código de trabajo mi str se asigna dinámicamente (datos Curl de la web).

    – bsteo

    14 de junio de 2013 a las 9:39

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

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

  • Es mejor obtener primero la longitud de la cadena y crear datos adecuados, ya que las personas tienen diferentes nombres y viven en diferentes ciudades.

    – aggsol

    14 de junio de 2013 a las 9:13

avatar de usuario
aggsol

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

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad