Leyendo CSV desde archivo de texto en C

Estoy intentando leer CSV desde un archivo de texto en C. El formato del archivo de texto es

1,Bob,bob@gmail.com 2,Daniel,daniel@gmail.com 3,John,john@gmail.com 

Cuando ejecuto el progtwig, el número se muestra bien, pero el nombre y el correo electrónico se muestran como basura. Aquí está mi progtwig …

 #include  #include  #include  typedef struct { int number; char* name; char* email; } Owner; Owner owners[100]; int load(char* filename) { char buffer[200]; char token[50]; Owner* owner; int owners_size = 0; FILE* file = fopen(filename, "r"); while(fgets(buffer, 200, file) != NULL) { owner = (Owner*)malloc(sizeof(Owner)); owner->number = atoi(strtok(buffer, ",")); owner->name = strtok(NULL, ","); owner->email = strtok(NULL, ","); owners[owners_size++] = *owner; } fclose(file); return owners_size; } int main() { int choise, owners_size, index; char* owners_filename = "owners2.txt"; owners_size = load(owners_filename); if(owners_size) { printf("owners size: %d\n\n", owners_size); for(index = 0; index < owners_size; index++) printf("%d, %s %s\n", owners[index].number, owners[index].name, owners[index].email); } } 

¿Alguien puede decirme cuál es la razón? Aprecio tu ayuda.

Acaba de almacenar los punteros en un búfer local. Cuando dejas load() este buffer se ha ido y ya no es accesible.

Debe asignar memoria para el name y el email antes de poder copiarla en la estructura Owner .

 char *tok; tok = strtok(NULL, ","); len = strlen(tok); owner->name = malloc(len + 1); strcpy(owner->name, tok); ... 

[EDIT: debe asignar len+1 bytes para que tenga espacio para el carácter NUL terminación. -Zack]

Dos problemas:

  1. No asignaste espacio para las cadenas en la estructura:

     typedef struct { int number; char *name; char *email; } Owner; 

    Debe proporcionar espacio para que los punteros apunten para contener los nombres.

  2. Continúa suministrando punteros al búfer que se reutiliza para cada línea de entrada:

     while(fgets(buffer, 200, file) != NULL) { owner = (Owner*)malloc(sizeof(Owner)); owner->number = atoi(strtok(buffer, ",")); owner->name = strtok(NULL, ","); owner->email = strtok(NULL, ","); owners[owners_size++] = *owner; } 

    La primera línea se almacena como algunos punteros en el búfer. La siguiente línea luego sobrescribe el búfer y corta la línea nuevamente, pisoteando toda la entrada original.

Considere el uso de strdup() :

 while (fgets(buffer, 200, file) != NULL) { owner = (Owner *)malloc(sizeof(Owner)); owner->number = atoi(strtok(buffer, ",")); owner->name = strdup(strtok(NULL, ",")); owner->email = strdup(strtok(NULL, ",")); owners[owners_size++] = *owner; } 

Este es un código ligeramente peligroso (no lo usaría en el código de producción) porque no comprueba que strtok() encontró un token cuando se esperaba (o que strdup() tuvo éxito). De nuevo, tampoco usaría strtok() en el código de producción; strtok_r() POSIX strtok_r() o Microsoft strtok_s() si estuvieran disponibles, o alguna técnica alternativa, probablemente usando strspn() y strcspn() . Si strdup() no está disponible, puede escribir el suyo propio, con el mismo nombre o un nombre diferente:

 char *strdup(const char *str) { size_t len = strlen(str) + 1; char *dup = malloc(len); if (dup != 0) memmove(dup, str, len); // Or memcpy() - that is safe in this context return(dup); } 

Es posible que tenga en cuenta que su código solo es adecuado para archivos CSV simples. Si encuentra una línea como esta (que es un CSV legítimo), tendrá problemas (con comillas en sus valores y división errónea debido a la coma dentro de la cadena entre comillas):

 1,"Bob ""The King"" King","Bob King, Itinerant Programmer " 

El puntero devuelto por strtok() apunta a una dirección dentro del búfer que está analizando, en este caso el buffer variable local. Cuando load() devuelve la variable, está fuera del scope (incluso si no todas las instancias de los owners apuntaran a la misma dirección). Necesitas copiar la cadena devuelta por strtok() . Puede usar strdup() si está disponible o usar malloc() y strcpy() .

No hay necesidad de que malloc() nuevas instancias de Owner ya que una matriz de ellas ya existe (el código actual tiene una pérdida de memoria).

Tenga en cuenta que no hay protección contra ir más allá de los límites de la matriz de owners . Si el archivo tiene más de 100 entradas, el bucle irá más allá de los límites de la matriz. Extienda la condición de terminación del while para evitar esto:

 while(owners_size < sizeof(owners) / sizeof(owners[0]) && fgets(buffer, 200, file) != NULL) { } 

Sólo tienes una línea de búfer. Cada ciclo del bucle en load obstruye el texto del ciclo anterior. Y si eso no fuera lo suficientemente malo, el búfer se destruye cuando vuelve la load .

La solución rápida es cambiar

 owner->name = strtok(NULL, ","); owner->email = strtok(NULL, ","); 

a

 owner->name = strdup(strtok(NULL, ",")); owner->email = strdup(strtok(NULL, ",")); 

(Si no tienes strdup , conseguir una computadora real es muy simple de escribir.

Sin embargo, si estuviera revisando su código, lo haría por el búfer de línea de tamaño fijo, la matriz de propietarios de tamaño fijo, la pérdida de memoria, usando atoi lugar de strtol , usando strtok lugar de strsep , y la ausencia de manejo de comillas y analizar la recuperación de errores, y señalar que sería más eficiente asignar cada línea como una unidad y luego guardar los punteros en ella.