0

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
Fenomatik
  • 457
  • 2
  • 8
  • 22
  • 4
    Your `declare_space_array()` function simply allocates and leaks memory. The variable `arr` is local to that function; it is not accessible outside that function. – Jonathan Leffler Nov 24 '16 at 06:19
  • @JonathanLeffler Can arr be made global ? or is there any other approach ? – Fenomatik Nov 24 '16 at 06:21
  • Note that [`while (!feof(file))` is always wrong](http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong). – Jonathan Leffler Nov 24 '16 at 06:21
  • 3
    It (`arr`) can be made global but shouldn't be. The function should return a `char *` and should return the allocated pointer (after checking that the allocation was successful). You'd then have `char *arr = declare_space_array(lines);` and you can use that in your `main()`, remembering to free the space at the end. I see you're trying to use it as a 2D array — though you allocated a simple 1D vector. That's trickier. You're going to have to worry about newlines at the end of each line (as well as the comma separators). They'll throw your character count out. – Jonathan Leffler Nov 24 '16 at 06:23
  • @JonathanLeffler Any chance you can write me the function to make arr global ? I am not very fluent with pointers. – Fenomatik Nov 24 '16 at 06:38
  • It's a bit hard for me to do it because I'm not clear how you want the array organized. You have lines like `strcpy(arr[s2][c2], token);` which imply that `arr` needs to be some 3D array (`char ***arr`). But you never reset `c2` to zero so it isn't clear how big the second dimension should be. It looks a bit as if you expect to have `s2` as a line number, `c2` as a column number, and the actual data will be a 3 byte name (apparently 'letter-digit-null'). Your printing loop supports this hypothesis, though it doesn't include any newlines so everything will appear on a single line. _…more…_ – Jonathan Leffler Nov 24 '16 at 06:43
  • 2
    On the other hand, your memory allocation is nowhere near big enough to support such a structure (you only allocate 3 bytes per line), and you only allocate a `char *`. That needn't be an insuperable problem — but there are multiple ways to fix it. Are you able to use C11 (or C99), or are you stuck with C90? How many marks will I get for my rendition of your homework? – Jonathan Leffler Nov 24 '16 at 06:45
  • Not homework :) , Trying to learn programming . Actually the input is an example the real data could be like Server , Location, Platform. – Fenomatik Nov 24 '16 at 06:50
  • In that case, you need a more sophisticated memory allocation scheme. So, each line will be three fields, but the fields can be of varying lengths and longer than the 2 character fields shown in the example? That alters the way the work should be done. Previous thought patterns had been running along the lines of dynamically allocating an array `char data[lines][3][3];`, but that's not appropriate if the third dimension (the second 3) can vary. You need a 2D array of pointers: `char *data[lines][3];` and then you need to duplicate each string in turn — look up `strdup()`. – Jonathan Leffler Nov 24 '16 at 06:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/128906/discussion-between-fenomatik-and-jonathan-leffler). – Fenomatik Nov 24 '16 at 06:55
  • I see from the file name that you're on a Windows platform. Which compiler are you using? I don't have access to a Windows machine so I don't know all the ins and outs of MS Visual Studio, etc. – Jonathan Leffler Nov 24 '16 at 06:56

1 Answers1

1

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278