Fenomatik Fenomatik - 9 days ago 4
C Question

copying tokens to 2D array using malloc

Struggling to move tokens to a 2D array .
The idea is that I am reading a file with multiple lines , get the number of lines and then based on that create a 2D array to use memory wisely(I dont want to create a 100 x 3 array for no reason).

I think I got the 2D array initialized in a separate funtion but when I try to enter data read from strtok() , I am getting error :

error: 'arr' undeclared (first use in this function)
strcpy(&arr[s2][c2],token);


Here is my code :

#include <stdio.h>
#include <string.h>
int ch, lines;

int no_of_lines(char* fp)
{
while(!feof(fp)) {
ch = fgetc(fp);
if(ch == '\n') {
lines++;
}
}
lines++;
return lines;
}

void declare_space_array(int size)
{
char* arr = (char*)malloc(size * 3 * sizeof(char));
return;
}

int main(void)
{
int c2 = 0;
int s2 = 0;
int len;
// char data[10][4];
static const char filename[] = "C:\\Users\\PC\\Documents\\Assignments\\stringops\\test.txt";
FILE* file = fopen(filename, "r");

no_of_lines(file);
printf("No of lines in file = %d", lines);
printf("\n");
// Closing file because it was read once till the end of file
fclose(file);
// Opening file again to read for parsing

file = fopen(filename, "r");
declare_space_array(lines);

char* token;

if(file != NULL) {
char line[128];
while(fgets(line, sizeof line, file) != NULL)
{
len = strlen(line);
printf("%d %s", len - 1, line);

const char s = ",";

token = strtok(line, ",");

while(token != NULL) {
strcpy(arr[s2][c2], token);
// printf( "%s\n", token );

token = strtok(NULL, ",");
c2++;
}
s2++;
}
fclose(file);
} else {
perror(filename); /* why didn't the file open? */
}

for(r1 = 0; r1 < lines; r1++) {
for(c1 = 0; c1 < 3; c1++) {
printf("%s", &arr[r1][c1]);
}
}
return 0;
}


file is something like this:

A1,B1,C1
A2,B2,C2
A3,B3,C3


EXPECTED OUTPUT TO SOMETHIGN LIKE THIS:

A1
B1
C1
A2
B2
C2
A3
B3
C3

Answer

After discussion in chat, etc, you could end up with code like this. This uses a global variable arr that's a pointer to an array of arrays of 3 char * values.

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

static int lines = 0;
static char *(*arr)[3] = 0; // global definition.

static int no_of_lines(FILE *fp)
{
    lines = 0;
    int ch;
    while ((ch = fgetc(fp)) != EOF)
    {
        if (ch == '\n')
            lines++;
    }
    return ++lines;     // Allow for last line possibly not having a newline
}

static void declare_space_array(int size)
{
    arr = calloc(size, 3 * sizeof(char *)); // zeroed memory allocation
    if (arr == 0)
    {
        fprintf(stderr, "Failed to allocate memory\n");
        exit(1);
    }
}

int main(void)
{
    int c2 = 0;
    int s2 = 0;
    int len;
    // char data[10][4];
    // static const char filename[] = "C:\\Users\\PC\\Documents\\Assignments\\stringops\\test.txt";
    const char *filename = "data";
    FILE *file = fopen(filename, "r");
    if (file == 0)
    {
        fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
        exit(1);
    }

    no_of_lines(file);
    printf("No of lines in file = %d\n", lines);
    rewind(file);

    declare_space_array(lines);

    const char delims[] = ",\n";

    char line[128];
    while (fgets(line, sizeof line, file) != NULL)
    {
        char *token;
        c2 = 0;
        len = strlen(line);
        printf("%d [%.*s]\n", len - 1, len - 1, line);

        token = strtok(line, delims);

        while (token != NULL)
        {
            arr[s2][c2] = strdup(token); // copy token (from strtok) into newly allocated string.
            token = strtok(NULL, delims);
            c2++;
        }
        s2++;
    }
    fclose(file);

    for (int r1 = 0; r1 < lines; r1++)
    {
        if (arr[r1][0] != 0)
        {
            for (int c1 = 0; c1 < 3; c1++)
                printf(" %-10s", arr[r1][c1]);
            putchar('\n');
        }
    }
    return 0;
}

It doesn't release the memory that's allocated — I got lazy.

Sample data (note that the names are longer than 2 characters and are of variable length):

server1,Phoenix,Windows
server2,Dallas,Linux
server-99,London,z/OS

Sample output:

No of lines in file = 4
23 [server1,Phoenix,Windows]
20 [server2,Dallas,Linux]
21 [server-99,London,z/OS]
 server1    Phoenix    Windows   
 server2    Dallas     Linux     
 server-99  London     z/OS      

The 'number of lines in file = 4' allows for the possibility that there isn't a newline at the end of the last line. The code in the printing loop allows for the possibility that there was a newline at the end and therefore the count is an over-estimate. It would spot a memory allocation from strdup() as long as the failure was on the first field of a line. It might crash if it was the second or third field that was not successfully copied.

Comments