Jack Johnson
Jack Johnson

Reputation: 25

Reading a 2D array from a file in C

I'm attempting to create a program to read a 2D array from a file and then print it out.

The file is set out so that the first line has the number of rows and then the number of columns. After that the array is plotted. An example is as follows:

3 5
10.4 15.1 18.5 13.3 20.8
76.5 55.3 94.0 48.5 60.3
2.4 4.6 3.5 4.6 8.9

My problem is that i only know how to read the first element of each line using fgets and sscanf so the following numbers are ignored.

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

int main(int argc, char* argv[]){

FILE* f = fopen("plottestdata", "r");
char size[20];
int height, width,ii=0,cc,jj,kk;
float array[100][100];
char horiz[500];

if(fgets(size, 20, f)!= NULL){

    sscanf(size,"%d %d", &height, &width);
    printf("%d %d\n",height, width);
}
while(fgets(horiz, 500, f)!=NULL)
{

    if(ii<height)
    {
        for(cc=0;cc<width;cc++)
        {
        sscanf(horiz, "%f", &array[ii][cc]);
        }
    ii++;
    }
}
for(jj=0;jj<width;jj++)
    {
        for(kk=0;kk<height;kk++)
        {
        printf("%f ", array[jj][kk]);
        }
    }
fclose(f);
return 0;
}

This gives me an output of the first element of each line repeated multiple times and I understand why but am unsure how to fix it. The file it is reading from is actually a 20x20 array although set out in the same format as the example.

Also, i have left out my error checking for the sake of trying to shortening a long question.

Upvotes: 1

Views: 19631

Answers (4)

Ampati Hareesh
Ampati Hareesh

Reputation: 1872

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

int main(int argc, char* argv[]){
    FILE* f = fopen("plottestdata", "r");
    char size[20];
    int height, width,ii=0,cc,jj,kk;
    char horiz[500];

    if(fgets(size, 500, f)!= NULL){

    sscanf(size,"%d %d", &height, &width);
    printf("%d %d\n",height, width);
    }
    float array[height][width];
    while(fgets(horiz, 500, f)!=NULL)
    {

    if(ii<height)
    {
         sscanf(horiz, "%f %f %f %f %f", &array[ii][0],&array[ii][1],
                    &array[ii][2],
                    &array[ii][3],
                    &array[ii][4]);       
              ii++;
        }
    }
    for(ii=0;ii<height;ii++)
    {
        for(kk=0;kk<width;kk++)
        {
            printf("%.2f ", array[ii][kk]);
        }
        printf("\n");
    }
    fclose(f);
    return 0;
}

Upvotes: 1

M Oehm
M Oehm

Reputation: 29126

As you have already found out, you need to scan the next value where you left off after scanning the previous one. Unfortunately, sscanf does not have the properties of a stream; it doesn't memorise its last position and always starts afresh if you pass the same string.

Fortunalely, there are many other ways to achieve sequential scanning.

Tokenisation You could tokenise your line forst and then parse each token as floating-point number. strtok from string.h> and strtod from <stdlib.h> will do that job for you.

        char *token = strtok(horiz, " \t\n");

        for (cc = 0; cc < width; cc++) {
            if (token) {                
                array[ii][cc] = strtod(token, NULL);
                token = strtok(NULL, " \t\n");
            } else {
                array[ii][cc] = 0.0;
            }
        }

Keep track of the tail I strtod has one other characteristic: It tells you exactly where parsing stopped when you supply a pointer to a char pointer. strtod also skips leading white space. You can use these two facts to parse floats separated by white space.

        char *p = horiz;

        for (cc = 0; cc < width; cc++) {
            char *tail;

            array[ii][cc] = strtod(p, &tail);
            p = tail;
        }

Keep track of the tail II sscanf has a special format that tells you how many characters have been parsed at a certain point in an integer. The above could be written with sscanf:

       char *p = horiz;

        for (cc = 0; cc < width; cc++) {
            int len;

            sscanf(p, "%f%n", &array[ii][cc], &len);
            p += len;
        }

All these work for your example, but none of them does proper error checking. The first example at least makes a half-hearted attempt to catch the case where a line has fewer tokens than the matrix has columns.

Upvotes: 1

Weather Vane
Weather Vane

Reputation: 34585

I could not resist tweaking and simplifying your code, it was so close, to avoid the use of fgets and use fscanf to read the values directly. Plus basic error checking.

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

#define MWIDTH  100
#define MHEIGHT 100

int main(void){

    FILE* f;
    int height, width, ii, jj;
    float array[MHEIGHT][MWIDTH];

    if((f = fopen("plottestdata.txt", "r")) == NULL)
        exit(1);

    if(fscanf(f, "%d%d", &height, &width) != 2)
        exit(1);
    if (height < 1 || height > MHEIGHT || width < 1 || width > MWIDTH)
        exit(1);

    for(jj=0; jj<height; jj++)
        for(ii=0; ii<width; ii++)
            if(fscanf(f, "%f", &array[jj][ii]) != 1)
                exit(1);
    fclose(f);

    for(jj=0; jj<height; jj++){
        for(ii=0; ii<width; ii++)
            printf ("%10.1f", array[jj][ii]);
        printf("\n");
    }
    return 0;
}

Program output:

  10.4      15.1      18.5      13.3      20.8
  76.5      55.3      94.0      48.5      60.3
   2.4       4.6       3.5       4.6       8.9

Upvotes: 3

Jens
Jens

Reputation: 72657

Instead of reading from a file with a hardcoded file name, why not read from stdin? You can use fgets(size, sizeof size, stdin) to read the first line with the dimensions.

Then use scanf(" %f", &array[ii][cc) to read in values one by one.

Be sure to test whether scanf returns 1 and add some error handling if not.

Upvotes: 0

Related Questions