Diagnosis
The fundamental problem you face is that you are saving pointers to the variable line
in your structures, but each new line overwrites what was previously in line
, so at the end, only data from the last line is present. It is fortuitous that your lines of data are all the same 'shape'; if the fields were of different lengths, you'd have more interesting, but equally erroneous, results.
Consequently, you need to save a copy of each field, not simply a pointer to the field. The simple way to do that is with POSIX function strdup()
. If you don't have the function, you can create it:
char *strdup(const char *str)
{
size_t len = strlen(str) + 1;
char *rv = malloc(len);
if (rv != 0)
memmove(rv, str, len); // or memcpy
return rv;
}
Your code doesn't compile; your data structure has elements x
and y
but your code uses elements Id
and act
. You use a VLA of pointers to your struct proc
, but it would be sensible to allocate an array of the structure, either as a VLA or via malloc()
et al. You should check memory allocations — there isn't a way to check VLAs, though (one reason to use dynamic allocation instead). You could rewind the file instead of reopening it. (It's a good idea to use a variable to hold the file name, even if you only open it once; it makes error reporting better. Also, errors should stop the program, in general, though you did use perror()
if the reopen operation failed — but not if the open failed.) You don't need two arrays into which to read the lines. It's a good idea to use far longer buffers for input lines. You should free dynamically allocated memory. Also, see What should main()
return in C and C++?; the answer is int
and not void
(unless perhaps you are on Windows).
Here are three variants of your code, with various aspects of the issues outlined above more or less fixed.
VLA of pointers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int rows = 0, rowCount = 0;
FILE *fpCount = fopen(datafile, "r");
if (fpCount == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char lineCount[2000];
while (fgets(lineCount, sizeof(lineCount), fpCount))
rows++;
fclose(fpCount);
printf("Read %d rows from '%s'\n", rows, datafile);
struct proc *pi[rows];
for (int i = 0; i < rows; i++)
pi[i] = (struct proc *)malloc(sizeof(struct proc));
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to reopen '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount]->x = strdup(token);
}
else if (tokenVal == 1)
{
pi[rowCount]->y = strdup(token);
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k]->x, pi[k]->y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k]->x);
free(pi[k]->y);
free(pi[k]);
}
return 0;
}
VLA of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int rows = 0, rowCount = 0;
FILE *fpCount = fopen(datafile, "r");
if (fpCount == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char lineCount[2000];
while (fgets(lineCount, sizeof(lineCount), fpCount))
rows++;
fclose(fpCount);
printf("Read %d rows from '%s'\n", rows, datafile);
struct proc pi[rows];
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to reopen '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount].x = strdup(token);
}
else if (tokenVal == 1)
{
pi[rowCount].y = strdup(token);
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k].x, pi[k].y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k].x);
free(pi[k].y);
}
return 0;
}
Dynamic array of structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct proc
{
char *x;
char *y;
};
int main(void)
{
const char datafile[] = "data.csv";
const char csv_delim[] = ",\n";
int num_rows = 0, rowCount = 0;
FILE *fp = fopen(datafile, "r");
if (fp == NULL)
{
fprintf(stderr, "Failed to open '%s' for reading\n", datafile);
exit(EXIT_FAILURE);
}
char line[2000];
while (fgets(line, sizeof(line), fp))
num_rows++;
rewind(fp);
printf("Read %d rows from '%s'\n", num_rows, datafile);
struct proc *pi = calloc(num_rows, sizeof(*pi));
if (pi == 0)
{
fprintf(stderr, "Failed to allocate %zu bytes of memory\n", num_rows * sizeof(*pi));
exit(EXIT_FAILURE);
}
while (fgets(line, sizeof(line), fp) != NULL)
{
printf("Start rowCount = %d\t", rowCount);
int tokenVal = 0;
char *token = strtok(line, csv_delim);
while (token != NULL)
{
if (tokenVal == 0)
{
pi[rowCount].x = strdup(token);
// null check
}
else if (tokenVal == 1)
{
pi[rowCount].y = strdup(token);
// null check
}
printf("[%s]", token);
tokenVal++;
token = strtok(NULL, csv_delim);
}
printf("\tend rowCount = %d\n", rowCount);
rowCount++;
}
fclose(fp);
/* Data validation */
printf("total %d\n", rowCount);
for (int k = 0; k < rowCount; k++)
{
printf("%d = [%s]----[%s]\n", k, pi[k].x, pi[k].y);
}
/* Release allocated memory */
for (int k = 0; k < rowCount; k++)
{
free(pi[k].x);
free(pi[k].y);
}
free(pi);
return 0;
}
Given a data file:
1,A,X
2,B,Y
3,C,Z
3192-2146-9913,Abelone,Zoophyte
all three programs produce the same output:
Read 4 rows from 'data.csv'
Start rowCount = 0 [1][A][X] end rowCount = 0
Start rowCount = 1 [2][B][Y] end rowCount = 1
Start rowCount = 2 [3][C][Z] end rowCount = 2
Start rowCount = 3 [3192-2146-9913][Abelone][Zoophyte] end rowCount = 3
total 4
0 = [1]----[A]
1 = [2]----[B]
2 = [3]----[C]
3 = [3192-2146-9913]----[Abelone]