1

I have a csv file having values

1,A,X
2,B,Y
3,C,Z

I have to read the CSV file line by line and keep it in a Structure array. The values are going fine each time in the for loop. But at the end when I am printing the Array, only the last value is being printed. Somebody please tell me where am I doing the logical error?

struct proc
{
    char *x;
    char *y;
};

void main()
{
    fflush(stdin);
    fflush(stdout);

    const char s[2] = ",";
    char *token;
    int rows=0,i,tokenVal=0,rowCount=0;
    FILE *fpCount = fopen("data.csv","r");
    if(fpCount != NULL)
    {
        char lineCount[20];
        while(fgets(lineCount, sizeof lineCount, fpCount))
            rows++;
    }
    struct proc *pi[rows];
    for(i=0;i<rows;i++)
        pi[i] = (struct proc*) malloc(sizeof(struct proc));
    FILE *fp = fopen("data.csv", "r");
    if(fp != NULL)
    {
        char line[20];        
        while(fgets(line, sizeof line, fp) != NULL)
        {
            printf("Start rowCount = %d\t",rowCount);
            token = strtok(line, s);
            while(token!=NULL)
            {
                if(tokenVal==0)
                    {
                        pi[rowCount]->Id =token;
                    }
                if(tokenVal==1)
                {
                    pi[rowCount]->act = token;
                }
                printf("\n");
                tokenVal++;
                token = strtok(NULL,s);
            }

            tokenVal = 0;
            printf("end rowCount = %d\t",rowCount);
            rowCount++;
        }
        fclose(fp);
    } else {
        perror("data.csv");
    }
    printf("total %d",rowCount);
    int k=0;
    for(k=0;k<rowCount;k++)
    {
        printf(" %d = %s----%s",k,pi[k]->Id,pi[k]->act);
    }
}
  • You read each line into `line`; you copy pointers into your array, all pointing at `line`. At the end of the input, `line` contains the remnants of the last line left by `strtok()` — so all the entries in the array are pointing at the last line. You need to duplicate (`strdup()` or equivalent) the values (tokens) to be saved into the array. – Jonathan Leffler Feb 04 '17 at 20:18
  • Note the details of [Using `fflush(stdin)`](http://stackoverflow.com/questions/2979209/using-fflushstdin); it is not portable. – Jonathan Leffler Feb 04 '17 at 20:19
  • This is Magic!!! Thank you so much. The Code is working fine now. – user3603139 Feb 05 '17 at 04:36

2 Answers2

0

In the printf(" %d = %s----%s----%s",k,pi[k]->Id,pi[k]->act); There are four data %d %s %s %s

but you set only three k pi[k]->Id pi[k]->act

Zenith
  • 697
  • 1
  • 5
  • 19
0

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]
Community
  • 1
  • 1
Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185