0

I have a file with several lines that look like this:

-180 -90 56 67
-179 -90 56 99
-178 -90 56 99
...
180 -90 45 45
-180 -89 45 45

The first two columns of each line are longitude and latitude values respectively. Now what I want to do is put these lines in a two dimensional array called filearrray[i][j] where i is longitude indices and j is latitude indices. Below is my attempt to do this:

int main(int argc, char *argv[])
{
    FILE    *fp=NULL;
    FILE    FILEOUT[150];
    char    inpFname[81],FILEOUT[150];
    char    buf[8000];
    char*   filearray = malloc(sizeof(float) * (159*360));
    int     i,j;
    int     date = atoi(argv[1]);   


    sprintf(inpFname,"global_20%d.test.out",date);
    if ((fp=fopen(inpFname,"rt"))==NULL)
    {
        printf("\nERROR: Cannot open/read input file [%s]\n\n",inpFname);
        exit(1);
    } 

    i=0;
    j=0;
    while(fgets(buf,sizeof buf, fp))
    {
        if( i >= 359 )
        {
            i=0;
            j=j+1;
        }
        sprintf(filearray[i][j],"%s",buf);
        i=i+1;
        

    }
}

However upon compiling the program I get the following error:

  translate_ww3file.c:34:23: error: subscripted value is neither array nor pointer nor     vector
       sprintf(filearray[i][j],"%s",buf);

How do I tweak this code so I can get the desired results?

3
  • 1
    Is it guaranteed that every longitude/latitude pair will occur exactly once, in a particular order? Or is it guaranteed that they will appear at most once? Or should your program check for duplicate entries and print an error message if such a duplicate entry occurs? Commented Jan 7, 2022 at 22:38
  • 3
    filearray is a char *. The compiler is telling you that filearray[i][j] is not valid. Commented Jan 7, 2022 at 22:41
  • 1
    What's FILE FILEOUT[150];? Commented Jan 7, 2022 at 22:45

1 Answer 1

2

The line

char*   filearray = malloc(sizeof(float) * (159*360));

is wrong, for several reasons:

In that line, you are allocating space for 159*360 elements of type float. However, you are assigning that space to a char *. This does not make sense. If every element in the 2D array should be a float, then you should write this instead:

float (*filearray)[159] = malloc( sizeof(float) * (360*159) );

In the line above, it is necessary to write float (*filearray)[159] instead of float *filearray[159], because the latter declares an array of 159 elements in which every element is a float*, whereas the former declares one pointer to an array of 159 elements in which every element is a float.

Also, the number 159 seems wrong. If latitudes are numbers between -90 and +90 inclusive, then there are 181 possible numbers, so you should be using the number 181 instead of 159. For the same reason, you should probably be using the number 361 instead of 360 for the longitude.

However, later in your program, you use the line:

sprintf(filearray[i][j],"%s",buf);

This implies that you don't want every element of the 2D array to be a float, because you can't sprintf to a float. Instead, it appears that you want every element to be a string.

If you want every element of the 2D array to be a string, then you must allocate sufficient space for every string. Simply allocating sizeof(float) bytes for every string will not be sufficient.

Since the two numbers that follow the longitude and latitude in every line seem to be integers, it may be appropriate to make every element of the 2D array store these two numbers as a pair of int, instead of using strings. You could define the following type:

struct pair
{
    int number1;
    int number2;
};

You could then define your 2D array like this:

struct pair (*filearray)[181] = malloc( sizeof(struct pair) * (361*181) );

It is also worth noting that it is good programming practice to call free for every malloc call when you no longer need the memory (in this case at the end of your program).

At the time of this writing, you have not yet responded to my request for clarification on whether it is guaranteed that every possible longitude/latitude combination will appear exactly once, and whether it will appear in-order. Therefore, I will assume that this is not guaranteed.

The following program will read one line at a time from the file and add it to the appropriate position in the 2D array, unless that longitude/latitude combination has already been encountered in the file, in which case it will print an error message instead. When it has finished reading the input, it will output the entire array contents, skipping longitude/latitude combinations that were not encountered in the input file (otherwise it would print 361*181 lines of output).

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

struct pair
{
    //false if this longitude/latitude combination has not yet
    //been found
    bool found;

    int number1;
    int number2;
};

int main( void )
{
    struct pair (*filearray)[181];
    char line[100];

    //allocate memory for 2D array
    //this uses calloc instead of malloc, so that the memory
    //is initialized to 0, so that the struct member "valid"
    //is initialized to false
    filearray = calloc( 361, sizeof *filearray );
    if ( filearray == NULL )
    {
        fprintf( stderr, "allocation error!\n" );
        exit( EXIT_FAILURE );
    }

    //read one line of input per loop iteration
    while ( fgets( line, sizeof line, stdin ) != NULL )
    {
        int longitude, latitude, number1, number2;

        //longitude and latitude converted to a valid positive index
        int longitude_index, latitude_index;

        //verify that an entire line was read
        if ( strchr( line, '\n' ) == NULL && !feof( stdin ) )
        {
            fprintf( stderr, "line too long!\n" );
            exit( EXIT_FAILURE );
        }

        //attempt to parse line
        if (
            sscanf(
                line,
                "%d %d %d %d",
                &longitude, &latitude, &number1, &number2
            )
            != 4
        )
        {
            printf( "skipping line due to invalid input!\n" );
            continue;
        }

        //verify that longitude is in proper range
        if ( ! ( -180 <= longitude && longitude <= +180 ) )
        {
            printf( "skipping line due to invalid longitude!\n" );
            continue;
        }

        //verify that latitude is in proper range
        if ( ! ( -90 <= latitude && latitude <= +90 ) )
        {
            printf( "skipping line due to invalid latitude!\n" );
            continue;
        }

        //convert longitude and latitude to a valid positive index
        //in the appropriate range, because negative indexes are
        //not permitted in C
        longitude_index = longitude + 180;
        latitude_index = latitude + 90;

        //verify that longitude/latitude combination has not already
        //been encountered
        if ( filearray[longitude_index][latitude_index].found )
        {
            printf(
                "combination %d %d has been encountered multiple times!\n",
                longitude, latitude
            );
            continue;
        }

        //input is ok, so write pair into 2D array
        filearray[longitude_index][latitude_index].found = true;
        filearray[longitude_index][latitude_index].number1 = number1;
        filearray[longitude_index][latitude_index].number2 = number2;
    }

    //print valid fields of 2D array
    for ( int i = 0; i < 361; i++ )
        for ( int j = 0; j < 181; j++ )
            if ( filearray[i][j].found )
                printf(
                    "long %d, lat %d : %d %d\n",
                    i - 180,
                    j - 90,
                    filearray[i][j].number1,
                    filearray[i][j].number2
                );

    free( filearray );
}

When running this program with the input

-180 -90 56 67
-179 -90 56 99
-178 -90 56 99
180 -90 45 45
-180 -89 45 45

which was taken from the question, this program has the following output:

long -180, lat -90 : 56 67
long -180, lat -89 : 45 45
long -179, lat -90 : 56 99
long -178, lat -90 : 56 99
long 180, lat -90 : 45 45
Sign up to request clarification or add additional context in comments.

5 Comments

Minor: Note that sizeof(float) * (360*159) is UB due to signed int overflow when int is 16-bit whereas sizeof(float) * 360 *159 less likely overflows. As size_t is overwhelming has more + range than int, better to find the product using size_t math throughout than int for a partial product.
Minor: for ( int i = 0; i < 361; i++ ) better as for ( int i = 0; i <= 360; i++ ) and not introduce another magic number. Yet good overall.
@chux: Regarding your first comment: Yes, you are correct that it would be safer to use size_t instead of int for the partial product. I took over that part of the code from OP's code, because I did not want to make too many changes that were not relevant to the point that I was making. I don't think that this is worth mentioning in the answer, because this will probably confuse OP more than help them. But it is worth mentioning in a comment.
@chux: Regarding your second comment: I'm afraid that I don't understand your comment. Are you saying that my code is good the way it is, or that it could be improved? I am already using 361 for the size of the array in my call to calloc, therefore it seems appropriate to use the same number in the for loop.
Rather than raw constants, code could have #define LONGITUDE_MAX 180 #define LONGITUDE_MIN -180 #define LONGITUDE_N (LONGITUDE_MAX - LONGITUDE_MIN + 1) and then int i = 0; i < LONGITUDE_N; i++, etc. Perhaps at a later time, code wanted to use _centi-degrees, then only need to change defines like #define LONGITUDE_MAX 1800. IAC, some minor ideas to an already UV'd answer.

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.