1

What am I trying:
To Load a csv file into memory of 2d array, then print the array to output.
The format in which I planned to load can be represented like the below give image:
pointer diagram of 2D array

In simple terms, the rows will be in a array where, each element of that array will be a pointer pointing to the columns of the row.
The columns will be an array where each element of that array will be pointing to the actual data.

Let me try explaining the blocks of my code before I post the actual code:
load_csv() : Function that accepts file name to load and returns a struct containing the 2d array, no of rows, no of columns. ( struct is named as FUNCTION_RETURN )
print() : Function to print array of characters.
copy_str() : Function that copies charecter from one location to other
print_data() : Function to print the 2d array
column_counter() : Function to count no of colums present in the csv file
free_procedure() : To free the 2d arrray from the memory after usage

Actual Code :

typedef struct function_return
{
    int rows;
    int columns;
    char*** data_structure;
} FUNCTION_RETURN;


void print(const char* string1)
{
    int iterator070 = 0;
    while(string1[iterator070] != '\0')
    {
        printf("%c",string1[iterator070]);
        iterator070 += 1;
    }
    
}


int copy_str(char* destination,char* source,int src_len)
{   
    int iterator = 0;

    
    while(iterator != (src_len + 1))
    {
        destination[iterator] = source[iterator];
        iterator += 1;  
    }
    destination[iterator] = '\0';
    return SUCCESS;
}

void print_data(FUNCTION_RETURN data)
{
    int row_iterator = 0;
    int column_iterator = 0;

    while(row_iterator < data.rows)
    {
        while(column_iterator < data.columns)
        {
            print(data.data_structure[row_iterator][column_iterator]);
            printf(",");
            column_iterator += 1;
        }
        printf("\n");
        row_iterator += 1;
    }

    printf("\n");
}


int column_counter(FILE* file_handle)
{
    int ch;
    int returnable = 0; //returns no of columns

    while((ch = fgetc(file_handle)) != '\n')
    {
        if(ch == ',')
        {
            returnable += 1;
        }
    }

    returnable += 1;
    fseek(file_handle,0,SEEK_SET); //set the seeker to the start of the file

    return returnable;
}


void free_procedure(char ***returnable,int rows,int columns)
{
    int row_iterator = 0, column_iterator = 0;

    while(row_iterator < rows)
    {
        while(column_iterator < columns)
        {
            free(returnable[row_iterator][column_iterator]);
            column_iterator += 1;
        }
        free(returnable[row_iterator]);
        row_iterator += 1;
    }

    free(returnable);

    printf("free_procedure done \n");
}

FUNCTION_RETURN load_data_CSV_v02(const char *Filename)
{
    //open file
    FILE *file_handle;
    if((file_handle = fopen(Filename,"r")) == NULL)
    {
        printf("cant open file\n");
        exit(1);
    }

    FUNCTION_RETURN returnable_structure;

    char ***returnable; //holds the actual data in a multidimentional structure in memory
    int row_iterator = 0;
    int column_iterator = 0;
    int no_of_rows = 1;
    int ch; //charecter iterator
    char file_buffer[200]; //will hold the text temporarily before transferring to returnable
    int file_buffer_iterator = 0; //iterator for file_buffer

    //create array of rows
    returnable = (char***)malloc(sizeof(char**) * no_of_rows);
    if(*returnable == NULL)
    {
        printf("error in allocating first row\n");
        exit(1);
    }

    //create array of columns for the first row
    returnable[row_iterator] = (char**)malloc(sizeof(char*) * (column_counter(file_handle) + 1));
    if(*returnable[row_iterator] == NULL)
    {
        printf("error in allocating first 4 columns\n");
        exit(1);
    }

    int column_counter_variable = column_counter(file_handle);
    //since column counter function seeks to the start of the file, using the function directly
    //in loop will cause to read the first row for all the iteration.



    while((ch = fgetc(file_handle)) != EOF)
    {
        printf("ch : %c\n",ch);

        if(ch == ',')
        {
            file_buffer[file_buffer_iterator] = '\0'; //null terminate the string
            printf("word recieved :");
            print(file_buffer);
            printf("\n");

            printf("row : %d , column : %d\n",row_iterator,column_iterator);

            returnable[row_iterator][column_iterator] = (char*)malloc(sizeof(char) * strlen(file_buffer));
            if(returnable[row_iterator][column_iterator] == NULL)
            {
                printf("error in allocating string");
                exit(1);
            }

            copy_str(returnable[row_iterator][column_iterator],file_buffer,strlen(file_buffer));
            printf("word found in structure :");
            print(returnable[row_iterator][column_iterator]);
            printf("\n");

            file_buffer_iterator = 0; //set iterator to 0 to start recieving next word
            column_iterator += 1;
        }
        else if(ch == '\n')
        {
            //end of column

            //first execute the same in if(ch == ',') to copy the word to the data_structure
            file_buffer[file_buffer_iterator] = '\0'; //null terminate the string
            printf("word recieved :");
            print(file_buffer);
            printf("\n");

            printf("row : %d , column : %d\n",row_iterator,column_iterator);

            returnable[row_iterator][column_iterator] = (char*)malloc(sizeof(char) * strlen(file_buffer));
            if(returnable[row_iterator][column_iterator] == NULL)
            {
                printf("error in allocating string");
                exit(1);
            }

            copy_str(returnable[row_iterator][column_iterator],file_buffer,strlen(file_buffer));
            printf("word found in structure :");
            print(returnable[row_iterator][column_iterator]);
            printf("\n");

            file_buffer_iterator = 0;

            //indicate the row is done
            returnable[row_iterator][column_iterator] = (char*)malloc(sizeof(char));
            *returnable[row_iterator][column_iterator] = '\n';

            //set values
            column_iterator = 0;
            row_iterator += 1;
            no_of_rows += 1;

            //realoc the row array
            returnable = (char***)realloc(returnable,sizeof(char**) * no_of_rows);
            if(*returnable == NULL)
            {
                printf("cant reallocate\n");
            }

            //allocate column array
            returnable[row_iterator] = (char**)malloc(sizeof(char*) * (column_counter_variable + 1));
            if(*returnable[row_iterator] == NULL)
            {
                printf("error in allocating the 4 columns\n");
                exit(1);
            }

        }
        else
        {
            //get a single word
            file_buffer[file_buffer_iterator] = ch;
            file_buffer_iterator += 1;
        }
    }

    printf("returnable before returned : \n");
    print_data2(returnable,no_of_rows,column_counter_variable);
    printf("\n-------------\n");

    print(returnable[2][3]);

    returnable_structure.rows = no_of_rows;
    returnable_structure.columns = column_counter_variable; //column_counter(file_handle);
    returnable_structure.data_structure = returnable;



    
    free_procedure(returnable,no_of_rows,column_counter_variable);
    

    printf("function ends\n");

    return returnable_structure;
}


int main()
{
    printf("log : (main) : start\n");
    FUNCTION_RETURN result = load_data_CSV_v02("data.csv");

    print_data(result);
    printf("what the heck is happening here :)");

    return 0;
}

As I have described what I intend to do and my code implementation, Let me tell you what is the issue here:
Take a look at the output
:

log : (main) : start
ch : R
ch : e
ch : d
ch : ,
word recieved :Red
row : 0 , column : 0
word found in structure :Red
ch : S
ch : p
ch : o
ch : r
ch : t
ch : s
ch : ,
word recieved :Sports
row : 0 , column : 1
word found in structure :Sports
ch : D
ch : o
ch : m
ch : e
ch : s
ch : t
ch : i
ch : c
ch : ,
word recieved :Domestic
row : 0 , column : 2
word found in structure :Domestic
ch : Y
ch : e
ch : s
ch :

word recieved :Yes
row : 0 , column : 3
word found in structure :Yes
ch : R
ch : e
ch : d
ch : ,
word recieved :Red
row : 1 , column : 0
word found in structure :Red
ch : S
ch : p
ch : o
ch : r
ch : t
ch : s
ch : ,
word recieved :Sports
row : 1 , column : 1
word found in structure :Sports
ch : D
ch : o
ch : m
ch : e
ch : s
ch : t
ch : i
ch : c
ch : ,
word recieved :Domestic
row : 1 , column : 2
word found in structure :Domestic
ch : N
ch : o
ch :

word recieved :No
row : 1 , column : 3
word found in structure :No
ch : R
ch : e
ch : d
ch : ,
word recieved :Red
row : 2 , column : 0
word found in structure :Red
ch : S
ch : p
ch : o
ch : r
ch : t
ch : s
ch : ,
word recieved :Sports
row : 2 , column : 1
word found in structure :Sports
ch : D
ch : o
ch : m
ch : e
ch : s
ch : t
ch : i
ch : c
ch : ,
word recieved :Domestic
row : 2 , column : 2
word found in structure :Domestic
ch : Y
ch : e
ch : s
ch :

word recieved :Yes
row : 2 , column : 3
word found in structure :Yes
ch : Y
ch : e
ch : l
ch : l
ch : o
ch : w
ch : ,
word recieved :Yellow
row : 3 , column : 0
word found in structure :Yellow
ch : S
ch : p
ch : o
ch : r
ch : t
ch : s
ch : ,
word recieved :Sports
row : 3 , column : 1
word found in structure :Sports
ch : D
ch : o
ch : m
ch : e
ch : s
ch : t
ch : i
ch : c
ch : ,
word recieved :Domestic
row : 3 , column : 2
word found in structure :Domestic
ch : N
ch : o
ch :

word recieved :No
row : 3 , column : 3
word found in structure :No
ch : Y
ch : e
ch : l
ch : l
ch : o
ch : w
ch : ,
word recieved :Yellow
row : 4 , column : 0
word found in structure :Yellow
ch : S
ch : p
ch : o
ch : r
ch : t
ch : s
ch : ,
word recieved :Sports
row : 4 , column : 1
word found in structure :Sports
ch : I
ch : n
ch : t
ch : e
ch : r
ch : n
ch : a
ch : t
ch : i
ch : o
ch : n
ch : a
ch : l
ch : ,
word recieved :International
row : 4 , column : 2
word found in structure :International
ch : Y
ch : e
ch : s
ch :

word recieved :Yes
row : 4 , column : 3
word found in structure :Yes
ch : Y
ch : e
ch : l
ch : l
ch : o
ch : w
ch : ,
word recieved :Yellow
row : 5 , column : 0
word found in structure :Yellow
ch : S
ch : U
ch : V
ch : ,
word recieved :SUV
row : 5 , column : 1
word found in structure :SUV
ch : I
ch : n
ch : t
ch : e
ch : r
ch : n
ch : a
ch : t
ch : i
ch : o
ch : n
ch : a
ch : l
ch : ,
word recieved :International
row : 5 , column : 2
word found in structure :International
ch : N
ch : o
ch :

word recieved :No
row : 5 , column : 3
word found in structure :No
ch : Y
ch : e
ch : l
ch : l
ch : o
ch : w
ch : ,
word recieved :Yellow
row : 6 , column : 0
word found in structure :Yellow
ch : S
ch : U
ch : V
ch : ,
word recieved :SUV
row : 6 , column : 1
word found in structure :SUV
ch : I
ch : n
ch : t
ch : e
ch : r
ch : n
ch : a
ch : t
ch : i
ch : o
ch : n
ch : a
ch : l
ch : ,
word recieved :International
row : 6 , column : 2
word found in structure :International
ch : Y
ch : e
ch : s
ch :

word recieved :Yes
row : 6 , column : 3
word found in structure :Yes
ch : Y
ch : e
ch : l
ch : l
ch : o
ch : w
ch : ,
word recieved :Yellow
row : 7 , column : 0
word found in structure :Yellow
ch : S
ch : U
ch : V
ch : ,
word recieved :SUV
row : 7 , column : 1
word found in structure :SUV
ch : D
ch : o
ch : m
ch : e
ch : s
ch : t
ch : i
ch : c
ch : ,
word recieved :Domestic
row : 7 , column : 2
word found in structure :Domestic
ch : N
ch : o
ch :

word recieved :No
row : 7 , column : 3
word found in structure :No
ch : R
ch : e
ch : d
ch : ,
word recieved :Red
row : 8 , column : 0
word found in structure :Red
ch : S
ch : U
ch : V
ch : ,
word recieved :SUV
row : 8 , column : 1
word found in structure :SUV
ch : I
ch : n
ch : t
ch : e
ch : r
ch : n
ch : a
ch : t
ch : i
ch : o
ch : n
ch : a
ch : l
ch : ,
word recieved :International
row : 8 , column : 2
word found in structure :International
ch : N
ch : o
ch :

word recieved :No
row : 8 , column : 3
word found in structure :No
ch : R
ch : e
ch : d
ch : ,
word recieved :Red
row : 9 , column : 0
word found in structure :Red
ch : S
ch : p
ch : o
ch : r
ch : t
ch : s
ch : ,
word recieved :Sports
row : 9 , column : 1
word found in structure :Sports
ch : I
ch : n
ch : t
ch : e
ch : r
ch : n
ch : a
ch : t
ch : i
ch : o
ch : n
ch : a
ch : l
ch : ,
word recieved :International
row : 9 , column : 2
word found in structure :International
ch : Y
ch : e
ch : s
returnable before returned :
row : 0
column : 0
Red,
column : 1
Sports,
column : 2
Domestic,
column : 3

rN┼┴,

row : 1

row : 2

row : 3

row : 4

row : 5

row : 6

row : 7

row : 8

row : 9



-------------

N┼┴free_procedure done
function ends
PN┼┴,`$N┼┴,Domestic, sN┼┴,










what the heck is happening here :)

As you can see, the output prints the stuff happening in loop that iterates through the csv file, Such as:

  1. The character that is being read from the csv file.
  2. The string present in the buffer before copying it to the 2d array. The string only gets printed if the loop encounters a ',' (comma) in the csv file
  3. The current row and column in which the loop is in
  4. The string present in the 2d array after copy_str() has copied the string from buffer to the array

After this, the array prints the array before returning the struct.
Then we print the array from main() function

What am I missing here ?, why is the data not printing correctly ? From the output, can I infer that I am accessing wrong memory locations out of the 2d array? and thats why am I seeing some symbols here?

For reference, Let me provide the csv file that I am working with:

Red,Sports,Domestic,Yes
Red,Sports,Domestic,No
Red,Sports,Domestic,Yes
Yellow,Sports,Domestic,No
Yellow,Sports,International,Yes
Yellow,SUV,International,No
Yellow,SUV,International,Yes
Yellow,SUV,Domestic,No
Red,SUV,International,No
Red,Sports,International,Yes

Also is this a good way to load csv into memory? Is there better ways?

Have a good day :)

8
  • Your immediate problem is print_data(returnable,no_of_rows,column_counter_variable); where returnable is char *** when print_data takes type FUNCTION_RETURN as the first argument. (your code has print_data2 - either a typo, or function you forgot to write) So yes, printing will be off. Also in C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?. There are a number of other suggestions, but fix the type issue first. Commented Jan 26 at 22:02
  • Hint: Always compile with warnings enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables and -Werror to treat warnings as errors). For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. The warnings will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you. Commented Jan 26 at 22:04
  • Why are you using returnable after free? E.g. returnable_structure.data_structure = returnable; then free_procedure(returnable,no_of_rows,column_counter_variable); Now when you return returnable_structure; and attempt to use it, returnable_structure.data_structure is a MEMORY USE AFTER FREE bug. Commented Jan 26 at 22:12
  • Your validations are off by 1-level of indirection. After allocating returnable = malloc (sizeof *returnable * no_of_rows); (fixed cast and using dereferenced pointer for type-size), your test should be if (returnable == NULL), NOT if (*returnable == NULL). You have the same problem with the returnable[row_iterator] allocation and test. Commented Jan 26 at 22:16
  • Your string allocations are too short by one. (you forget to provide storage for the nul-character '\0'), e.g. you have returnable[row_iterator][column_iterator] = (char*)malloc(sizeof(char) * strlen(file_buffer)); which should be returnable[row_iterator][column_iterator] = malloc (strlen(file_buffer) + 1); NOTE, sizeof(char) is defined as 1 so just remove it, it is superfluous. Commented Jan 26 at 22:24

1 Answer 1

4

First, for the most part, good job! Your basic understanding of dynamic memory management is coming along just fine, your implementation of all details needs more practice -- but that is where getting better comes from, more practice. On balance this was a good attempt at the problems, but there were a large number of small problems that crept in when you were putting all the pieces together. Summarizing what I found above in the comments, and in addition to that:

General Issues

  • Compile with full warnings enabled - Always. For gcc/clang, use -Wall -Wextra -pedantic -Wshadow -Werror as part of your compile string. If you are using Microsoft VS (cl.exe), then /W3 will do. Read and understand all warnings and go fix them. You will learn a lot that way.
  • Clean your strings before taking the length to allocate. Do it the same way every time. Don't take shortcuts like knowing the ',' or '\n' gives an extra character in length and then try and filter that through your copy -- there is too much room for error. Clean up your strings to what you will store, then take the length, allocate and copy. This led to your strings being too short by 1 byte in several places,
  • In C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?,
  • Don't include sizeof(char) in the computation for allocation size. sizeof(char) is defined as 1 so it is superfluous in any multiplication. If you want to keep it there as a reminder while you are learning, that's fine, but be aware it will raise eyebrows when others are looking at your code.

Specific Program Issues

  • Your print_data() function takes type FUNCTION_RETURN, but you were attempting to pass char ***. The warnings will catch this and give you a chance to fix it. Further, your int column_iterator = 0; must be inside the while() loop so it is reset to 0 for each row. (you have the same column_iterator problem in your free_procedure() function), example fix:
void print_data (FUNCTION_RETURN data)
{
    int row_iterator = 0;

    while(row_iterator < data.rows)
    {
        int column_iterator = 0;    /* must set column_iterator 0 each loop */
        
        while(column_iterator < data.columns)
        {
            print(data.data_structure[row_iterator][column_iterator]);
            printf(",");
            column_iterator += 1;
        }
        printf("\n");
        row_iterator += 1;
    }

    printf("\n");
}
  • You cannot assign the memory block pointed to by returnable to returnable_structure.data_structure and then call free_procedure(returnable,...). This frees the memory you just allocated. When you assign returnable_structure.data_structure = returnable; you are simply assigning the address held by returnable to the pointer returnable_structure.data_structure. They hold the same address. If you then free the memory at that address, it can no longer be accessed - period, no matter what pointer you use. It is the block of memory at that address that is freed.
  • Your validation of the memory you allocated was off by one-level of indirection. When you allocate for a pointer, e.g. pointer = malloc (bytes), you then check if (pointer == NULL), not if (*pointer == NULL). You have that in several places in your code,
  • When allocating for storage through a pointer with multiple levels of indirection, such as char **p; get in the habit of using the dereferenced pointer to set the type size., e.g. p = malloc (sizeof *p * bytes); By using the dereferenced pointer, you will always get the type-size right,
  • Mind your indexes and counts. In your load_data_CSV_v02() function you return 11 rows when there are only 10 which correspond to pointer-indexes 0-9. Your problem arises with int no_of_rows = 1; done as a crutch to make the allocation work. When the function is entered the number of rows (and index) is 0. This no_of_rows is also used to set the rows member. You can keep separate counters and indexes, but be careful doing so to make sure you don't use a count as an index and vice-versa or you will be off-by-one,
  • the load_data_CSV_v02() function has many other small logic errors, and yes there is a better way to separate your csv file into words -- read a line at a time with fgets() and then tokenize the line into words with strtok(), e.g.
FUNCTION_RETURN load_csv (const char *fname)
{
  FUNCTION_RETURN rs = { .data_structure = NULL };
  char file_buffer[MAXC] = "";
  FILE *fp = fopen (fname, "r");
  
  /* validate file open */
  if (fname == NULL) {
    perror ("load_csv fopen failed");
    return rs;
  }
  rs.columns = column_counter (fp);   /* retrieve column count */
  
  while (fgets (file_buffer, sizeof file_buffer, fp)) {
    char *tok = NULL;                 /* pointer for token */
    int column_iterator = 0;          /* column_iterator */
    
    /* allocate storage for row, always realloc to temporary pointer */
    void *tmp = realloc (rs.data_structure, 
                         sizeof *rs.data_structure * (rs.rows + 1));
    if (tmp == NULL) {
      perror ("load_csv realloc rs.data_structure");
      return rs;
    }
    rs.data_structure = tmp;        /* assign reallocated block to pointer */
    
    /* allocate storage for columns/validate */
    rs.data_structure[rs.rows] = malloc (sizeof *rs.data_structure[rs.rows] * 
                                         rs.columns);
    if (rs.data_structure[rs.rows] == NULL) {
      perror ("load_csv malloc rs.data_structure[rows]");
      return rs;
    }
    
    tok = strtok (file_buffer, ",\n");
    while (tok != NULL && column_iterator < rs.columns) {
      size_t len = strlen (tok);    /* get token length */
      /* allocate/validate storage for token */
      rs.data_structure[rs.rows][column_iterator] = malloc (len + 1);
      if (rs.data_structure[rs.rows][column_iterator] == NULL) {
        perror ("load_csv malloc rs.data_structure[rs.rows][column_iterator]");
        free (rs.data_structure[rs.rows]);  /* free mem for row */
        return rs;
      }
      /* copy from token to row/column storage in datastruct */
      memcpy (rs.data_structure[rs.rows][column_iterator], tok, len + 1);
      
      column_iterator += 1;
      tok = strtok (NULL, ",\n");
    }
    
    /* validate all tokens processed */
    if (column_iterator != rs.columns) {
      fprintf (stderr, "error: invalid column count (%d) row %d\n",
                column_iterator, rs.rows);
      for (int i = 0; i < column_iterator; i++) {
        free (rs.data_structure[rs.rows][i]);   /* free mem for partial cols */
      }
      free (rs.data_structure[rs.rows]);        /* free mem for row */
      return rs;
    }
    
    rs.rows += 1;     /* increment row count after all checks passed */
  }
  fclose (fp);  /* close file */
  
  return rs;    /* return pointer to filled struct */
}

note: I shortened the struct name to rs from returnable_structure just because I don't like to type -- long names (within reason) are fine.

You can read a character-at-a-time as you did in load_data_CSV_v02(), but notice how long the function is with the if ... then ... and all the debug. When you notice functions getting too long and complicated -- that means it is time to refactor. You could have pulled the character-by-character read into a read_word() function and made load_data_CSV_v02() easier to understand. That will come with "practice, practice, practice".

For comparison, a working versions of your load_data_CSV_v02() with the logic errors fixed follows. Note: there is no need to allocate an additional column for a '\n' at the end of each row. That code is commented out below (as well as the free_procedure() that caused your problem):

FUNCTION_RETURN load_data_CSV_v02(const char *Filename)
{
    //open file
    FILE *file_handle;
    if((file_handle = fopen(Filename,"r")) == NULL)
    {
        printf("cant open file\n");
        exit(1);
    }

    FUNCTION_RETURN returnable_structure;

    char ***returnable; //holds the actual data in a multidimentional structure in memory
    int row_iterator = 0;
    int column_iterator = 0;
    int no_of_rows = 0;
    int ch; //charecter iterator
    char file_buffer[200]; //will hold the text temporarily before transferring to returnable
    int file_buffer_iterator = 0; //iterator for file_buffer

    //create array of rows
    returnable = malloc (sizeof *returnable);
    if (returnable == NULL)
    {
        printf("error in allocating first row\n");
        exit(1);
    }

    //create array of columns for the first row
    returnable[row_iterator] = malloc (sizeof *returnable[row_iterator] * 
                                       column_counter(file_handle));
    if(returnable[row_iterator] == NULL)
    {
        printf("error in allocating first 4 columns\n");
        exit(1);
    }

    int column_counter_variable = column_counter(file_handle);
    //since column counter function seeks to the start of the file, using the function directly
    //in loop will cause to read the first row for all the iteration.



    while((ch = fgetc(file_handle)) != EOF)
    {
        size_t len = 0;
#ifdef DEBUG
        printf("ch : %c\n",ch);
#endif
        if(ch == ',')
        {
            file_buffer[file_buffer_iterator] = 0; //null terminate the string
            file_buffer[strcspn(file_buffer, ",\n")] = 0;
            len = strlen (file_buffer);
            
            printf("word recieved :");
            print(file_buffer);
            printf("\n");

            printf("row : %d , column : %d\n",row_iterator,column_iterator);

            returnable[row_iterator][column_iterator] = malloc (len + 1);
            if(returnable[row_iterator][column_iterator] == NULL)
            {
                printf("error in allocating string");
                exit(1);
            }

            // copy_str(returnable[row_iterator][column_iterator],file_buffer,strlen(file_buffer));
            memcpy (returnable[row_iterator][column_iterator], file_buffer, len + 1);
            printf("word found in structure :");
            print(returnable[row_iterator][column_iterator]);
            printf("\n");

            file_buffer_iterator = 0; //set iterator to 0 to start recieving next word
            column_iterator += 1;
        }
        else if(ch == '\n')
        {
            //end of column

            //first execute the same in if(ch == ',') to copy the word to the data_structure
            file_buffer[file_buffer_iterator] = 0; //null terminate the string
            file_buffer[strcspn(file_buffer, ",\n")] = 0;
            len = strlen (file_buffer);
            printf("word recieved :");
            print(file_buffer);
            printf("\n");

            printf("row : %d , column : %d\n",row_iterator,column_iterator);

            returnable[row_iterator][column_iterator] = malloc (strlen(file_buffer) + 1);
            if(returnable[row_iterator][column_iterator] == NULL)
            {
                printf("error in allocating string");
                exit(1);
            }

            // copy_str(returnable[row_iterator][column_iterator],file_buffer,strlen(file_buffer));
            memcpy (returnable[row_iterator][column_iterator], file_buffer, len + 1);
            printf("word found in structure :");
            print(returnable[row_iterator][column_iterator]);
            printf("\n");

            file_buffer_iterator = 0;

            //indicate the row is done
            /*
            returnable[row_iterator][column_iterator] = (char*)malloc(sizeof(char));
            *returnable[row_iterator][column_iterator] = '\n';
            */
            
            //set values
            column_iterator = 0;
            row_iterator += 1;
            no_of_rows += 1;

            //realoc the row array
            returnable = realloc (returnable, sizeof *returnable * (no_of_rows + 1));
            if(*returnable == NULL)
            {
                printf("cant reallocate\n");
            }

            //allocate column array
            returnable[row_iterator] = malloc(sizeof *returnable[row_iterator] * (column_counter_variable + 1));
            if(returnable[row_iterator] == NULL)
            {
                printf("error in allocating the 4 columns\n");
                exit(1);
            }

        }
        else
        {
            //get a single word
            file_buffer[file_buffer_iterator] = ch;
            file_buffer_iterator += 1;
        }
    }
    fclose (file_handle);

    returnable_structure.rows = no_of_rows;
    returnable_structure.columns = column_counter_variable; //column_counter(file_handle);
    returnable_structure.data_structure = returnable;

#ifdef DEBUG
    print(returnable[2][3]);

    printf("returnable before returned : \n");
    print_data(returnable_structure);
    printf("\n-------------\n");

    
    // free_procedure(returnable,no_of_rows,column_counter_variable);
    

    printf("function ends\n");
#endif

    return returnable_structure;
}

To prevent excess output, your debug statements were wrapped in the preprocessor define DEBUG so they could be turned off. To enable them, just pass -DDEBUG as part of your compiler string to define DEBUG and they will print. A similar USEV02 is used in main() to select between the different read csv functions.

Also in main() below, pass the filename to read as an argument. You shouldn't have to recompile your code just to read from a different filename. You can provide a data filename as a default while still preserving the ability to read from any filename.

Putting all the pieces together and fixing the logic errors, you can do something like the following:

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

#define SUCCESS 0
#define MAXC    1024

typedef struct function_return
{
    int rows;
    int columns;
    char ***data_structure;
} FUNCTION_RETURN;


void print (const char* string1)
{
    int iterator070 = 0;
    while(string1[iterator070] != '\0')
    {
        printf("%c",string1[iterator070]);
        iterator070 += 1;
    }
    
}


/** use memcpy or strcpy 
int copy_str(char* destination,char* source,int src_len)
{   
    int iterator = 0;

    
    while(iterator != (src_len + 1))
    {
        destination[iterator] = source[iterator];
        iterator += 1;  
    }
    destination[iterator] = '\0';
    return SUCCESS;
}
*/


void print_data (FUNCTION_RETURN data)
{
    int row_iterator = 0;

    while(row_iterator < data.rows)
    {
        int column_iterator = 0;    /* must set column_iterator 0 each loop */
        
        while(column_iterator < data.columns)
        {
            print(data.data_structure[row_iterator][column_iterator]);
            printf(",");
            column_iterator += 1;
        }
        printf("\n");
        row_iterator += 1;
    }

    printf("\n");
}


int column_counter (FILE* file_handle)
{
    int ch;
    int returnable = 0; //returns no of columns

    while((ch = fgetc(file_handle)) != '\n')
    {
        if(ch == ',')
        {
            returnable += 1;
        }
    }

    returnable += 1;
    fseek(file_handle,0,SEEK_SET); //set the seeker to the start of the file

    return returnable;
}


void free_procedure(char ***returnable,int rows,int columns)
{
    int row_iterator = 0;

    while(row_iterator < rows)
    {
        int column_iterator = 0;    /* must set column_iterator 0 each loop */
        
        while(column_iterator < columns)
        {
            free(returnable[row_iterator][column_iterator]);
            column_iterator += 1;
        }
        free(returnable[row_iterator]);
        row_iterator += 1;
    }

    free(returnable);
#ifdef DEBUG
    printf("free_procedure done \n");
#endif
}


FUNCTION_RETURN load_csv (const char *fname)
{
  FUNCTION_RETURN rs = { .data_structure = NULL };
  char file_buffer[MAXC] = "";
  FILE *fp = fopen (fname, "r");
  
  /* validate file open */
  if (fname == NULL) {
    perror ("load_csv fopen failed");
    return rs;
  }
  rs.columns = column_counter (fp);   /* retrieve column count */
  
  while (fgets (file_buffer, sizeof file_buffer, fp)) {
    char *tok = NULL;                 /* pointer for token */
    int column_iterator = 0;          /* column_iterator */
    
    /* allocate storage for row, always realloc to temporary pointer */
    void *tmp = realloc (rs.data_structure, 
                         sizeof *rs.data_structure * (rs.rows + 1));
    if (tmp == NULL) {
      perror ("load_csv realloc rs.data_structure");
      return rs;
    }
    rs.data_structure = tmp;        /* assign reallocated block to pointer */
    
    /* allocate storage for columns/validate */
    rs.data_structure[rs.rows] = malloc (sizeof *rs.data_structure[rs.rows] * 
                                         rs.columns);
    if (rs.data_structure[rs.rows] == NULL) {
      perror ("load_csv malloc rs.data_structure[rows]");
      return rs;
    }
    
    tok = strtok (file_buffer, ",\n");
    while (tok != NULL && column_iterator < rs.columns) {
      size_t len = strlen (tok);    /* get token length */
      /* allocate/validate storage for token */
      rs.data_structure[rs.rows][column_iterator] = malloc (len + 1);
      if (rs.data_structure[rs.rows][column_iterator] == NULL) {
        perror ("load_csv malloc rs.data_structure[rs.rows][column_iterator]");
        free (rs.data_structure[rs.rows]);  /* free mem for row */
        return rs;
      }
      /* copy from token to row/column storage in datastruct */
      memcpy (rs.data_structure[rs.rows][column_iterator], tok, len + 1);
      
      column_iterator += 1;
      tok = strtok (NULL, ",\n");
    }
    
    /* validate all tokens processed */
    if (column_iterator != rs.columns) {
      fprintf (stderr, "error: invalid column count (%d) row %d\n",
                column_iterator, rs.rows);
      for (int i = 0; i < column_iterator; i++) {
        free (rs.data_structure[rs.rows][i]);   /* free mem for partial cols */
      }
      free (rs.data_structure[rs.rows]);        /* free mem for row */
      return rs;
    }
    
    rs.rows += 1;     /* increment row count after all checks passed */
  }
  fclose (fp);  /* close file */
  
  return rs;    /* return pointer to filled struct */
}

FUNCTION_RETURN load_data_CSV_v02(const char *Filename)
{
    //open file
    FILE *file_handle;
    if((file_handle = fopen(Filename,"r")) == NULL)
    {
        printf("cant open file\n");
        exit(1);
    }

    FUNCTION_RETURN returnable_structure;

    char ***returnable; //holds the actual data in a multidimentional structure in memory
    int row_iterator = 0;
    int column_iterator = 0;
    int no_of_rows = 0;
    int ch; //charecter iterator
    char file_buffer[200]; //will hold the text temporarily before transferring to returnable
    int file_buffer_iterator = 0; //iterator for file_buffer

    //create array of rows
    returnable = malloc (sizeof *returnable);
    if (returnable == NULL)
    {
        printf("error in allocating first row\n");
        exit(1);
    }

    //create array of columns for the first row
    returnable[row_iterator] = malloc (sizeof *returnable[row_iterator] * 
                                       column_counter(file_handle));
    if(returnable[row_iterator] == NULL)
    {
        printf("error in allocating first 4 columns\n");
        exit(1);
    }

    int column_counter_variable = column_counter(file_handle);
    //since column counter function seeks to the start of the file, using the function directly
    //in loop will cause to read the first row for all the iteration.



    while((ch = fgetc(file_handle)) != EOF)
    {
        size_t len = 0;
#ifdef DEBUG
        printf("ch : %c\n",ch);
#endif
        if(ch == ',')
        {
            file_buffer[file_buffer_iterator] = 0; //null terminate the string
            file_buffer[strcspn(file_buffer, ",\n")] = 0;
            len = strlen (file_buffer);
            
            printf("word recieved :");
            print(file_buffer);
            printf("\n");

            printf("row : %d , column : %d\n",row_iterator,column_iterator);

            returnable[row_iterator][column_iterator] = malloc (len + 1);
            if(returnable[row_iterator][column_iterator] == NULL)
            {
                printf("error in allocating string");
                exit(1);
            }

            // copy_str(returnable[row_iterator][column_iterator],file_buffer,strlen(file_buffer));
            memcpy (returnable[row_iterator][column_iterator], file_buffer, len + 1);
            printf("word found in structure :");
            print(returnable[row_iterator][column_iterator]);
            printf("\n");

            file_buffer_iterator = 0; //set iterator to 0 to start recieving next word
            column_iterator += 1;
        }
        else if(ch == '\n')
        {
            //end of column

            //first execute the same in if(ch == ',') to copy the word to the data_structure
            file_buffer[file_buffer_iterator] = 0; //null terminate the string
            file_buffer[strcspn(file_buffer, ",\n")] = 0;
            len = strlen (file_buffer);
            printf("word recieved :");
            print(file_buffer);
            printf("\n");

            printf("row : %d , column : %d\n",row_iterator,column_iterator);

            returnable[row_iterator][column_iterator] = malloc (strlen(file_buffer) + 1);
            if(returnable[row_iterator][column_iterator] == NULL)
            {
                printf("error in allocating string");
                exit(1);
            }

            // copy_str(returnable[row_iterator][column_iterator],file_buffer,strlen(file_buffer));
            memcpy (returnable[row_iterator][column_iterator], file_buffer, len + 1);
            printf("word found in structure :");
            print(returnable[row_iterator][column_iterator]);
            printf("\n");

            file_buffer_iterator = 0;

            //indicate the row is done
            /*
            returnable[row_iterator][column_iterator] = (char*)malloc(sizeof(char));
            *returnable[row_iterator][column_iterator] = '\n';
            */
            
            //set values
            column_iterator = 0;
            row_iterator += 1;
            no_of_rows += 1;

            //realoc the row array
            returnable = realloc (returnable, sizeof *returnable * (no_of_rows + 1));
            if(*returnable == NULL)
            {
                printf("cant reallocate\n");
            }

            //allocate column array
            returnable[row_iterator] = malloc(sizeof *returnable[row_iterator] * (column_counter_variable + 1));
            if(returnable[row_iterator] == NULL)
            {
                printf("error in allocating the 4 columns\n");
                exit(1);
            }

        }
        else
        {
            //get a single word
            file_buffer[file_buffer_iterator] = ch;
            file_buffer_iterator += 1;
        }
    }
    fclose (file_handle);

    returnable_structure.rows = no_of_rows;
    returnable_structure.columns = column_counter_variable; //column_counter(file_handle);
    returnable_structure.data_structure = returnable;

#ifdef DEBUG
    print(returnable[2][3]);

    printf("returnable before returned : \n");
    print_data(returnable_structure);
    printf("\n-------------\n");

    
    // free_procedure(returnable,no_of_rows,column_counter_variable);
    

    printf("function ends\n");
#endif

    return returnable_structure;
}


int main (int argc, char **argv)
{
  const char *fname = argc > 1 ? argv[1] : "data.csv";
#ifdef USEV02  
  FUNCTION_RETURN result = load_data_CSV_v02 (fname);
#else
  FUNCTION_RETURN result = load_csv (fname);
#endif

  printf ("log : (main) : start\n");
  
  print_data (result);
  
  printf ("rows: %d\ncols: %d\n\n", result.rows, result.columns);
  
  free_procedure (result.data_structure, result.rows, result.columns);

  return 0;
}

Example Use/Modified Output

Compiling the code above and using your data file in dat/2dstr.csv you get the following:

$ ./bin/csv2dstrings dat/2dstr.csv
log : (main) : start
Red,Sports,Domestic,Yes,
Red,Sports,Domestic,No,
Red,Sports,Domestic,Yes,
Yellow,Sports,Domestic,No,
Yellow,Sports,International,Yes,
Yellow,SUV,International,No,
Yellow,SUV,International,Yes,
Yellow,SUV,Domestic,No,
Red,SUV,International,No,
Red,Sports,International,Yes,

rows: 10
cols: 4

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ valgrind ./bin/csv2dstrings dat/2dstr.csv
==149115== Memcheck, a memory error detector
==149115== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==149115== Using Valgrind-3.23.0 and LibVEX; rerun with -h for copyright info
==149115== Command: ./bin/csv2dstrings dat/2dstr.csv
==149115==
log : (main) : start
Red,Sports,Domestic,Yes,
Red,Sports,Domestic,No,
Red,Sports,Domestic,Yes,
Yellow,Sports,Domestic,No,
Yellow,Sports,International,Yes,
Yellow,SUV,International,No,
Yellow,SUV,International,Yes,
Yellow,SUV,Domestic,No,
Red,SUV,International,No,
Red,Sports,International,Yes,

rows: 10
cols: 4

==149115==
==149115== HEAP SUMMARY:
==149115==     in use at exit: 0 bytes in 0 blocks
==149115==   total heap usage: 63 allocs, 63 frees, 6,615 bytes allocated
==149115==
==149115== All heap blocks were freed -- no leaks are possible
==149115==
==149115== For lists of detected and suppressed errors, rerun with: -s
==149115== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Your Trailing Comma Problem

You also hinted about curing your print problem where you append an additional ',' at the end of every line that isn't there in the data file. To fix the problem, you turn your logic around. You don't think about printing a comma AFTER each word, you think about printing a comma BEFORE every word that isn't the first-word. Things work out much nicer that way, e.g.

void print_data (FUNCTION_RETURN data)
{
    int row_iterator = 0;

    while(row_iterator < data.rows)
    {
        int column_iterator = 0;    /* must set column_iterator 0 each loop */
        
        while(column_iterator < data.columns)
        {
            /* if not 1st column, add comma BEFORE */
            if (column_iterator) {
              putchar (',');
            }
            print(data.data_structure[row_iterator][column_iterator]);
            column_iterator += 1;
        }
        putchar ('\n');       /* you putchar() a single char, not printf() */
        row_iterator += 1;
    }

    putchar ('\n');
}

Output With Comma Fixed

Now the output reproduces what was in the data file without the extra trailing comma:

$ ./bin/csv2dstrings dat/2dstr.csv
log : (main) : start
Red,Sports,Domestic,Yes
Red,Sports,Domestic,No
Red,Sports,Domestic,Yes
Yellow,Sports,Domestic,No
Yellow,Sports,International,Yes
Yellow,SUV,International,No
Yellow,SUV,International,Yes
Yellow,SUV,Domestic,No
Red,SUV,International,No
Red,Sports,International,Yes

rows: 10
cols: 4

Let me know if you have further questions. You made a good first attempt at solving the problem and you showed a good base of understanding. The rest will come with practice.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.