1

I'm a little rusty on C but I have been working on a program that needs to read from two files and compare values/do math. I'm trying to make a struct for each file, skipping the first few lines because they contain strings. However, whenever I try to do this I only get zeros in the struct. I'm not sure why this keeps happening. I think it might be something with malloc and the pointers. Thanks for your help.

also I will attach an example of the file I'm reading.

txt file

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
 
typedef struct fcat_s{
    float x;
    float y;
    float a_j2000;
    float b_j2000;
    float mag;
} fcat_s;
 
typedef struct cat_s{
    float num;
    float x;
    float y;
    float xworld;
    float yworld;
    float flux_auto;
    float mag_auto;
    float awin;
    float bwin;
} cat_s;
 
void readFCAT(FILE *fcat, fcat_s *f, int fcatcount){
    int i;
    for (i=0;i<(fcatcount);i++){
        if (i>5){
            fscanf(fcat, "%f", &f[i-5].x);
            fscanf(fcat, "%f", &f[i-5].y);
            fscanf(fcat, "%f", &f[i-5].a_j2000);
            fscanf(fcat, "%f", &f[i-5].b_j2000);
            fscanf(fcat, "%f", &f[i-5].mag);
        }
    }
}
 
void readCAT(FILE *cat, cat_s *c, int catcount){
    int j;
    for (j=0;j<(catcount);j++){
        if (j>9){
            fscanf(cat, "%f", &c[j-9].num);
            fscanf(cat, "%f", &c[j-9].x);
            fscanf(cat, "%f", &c[j-9].y);
            fscanf(cat, "%f", &c[j-9].xworld);
            fscanf(cat, "%f", &c[j-9].yworld);
            fscanf(cat, "%f", &c[j-9].flux_auto);
            fscanf(cat, "%f", &c[j-9].mag_auto);
            fscanf(cat, "%f", &c[j-9].awin);
            fscanf(cat, "%f", &c[j-9].bwin);
        }
    }
}
 
void printFCAT(fcat_s *f, int fcatcount){
    int i=0;
    for(i=0;(i<(fcatcount-5));i++){
        printf("%lf\t %lf\t %lf\t %lf\t %lf\n", f[i].x, f[i].y, f[i].a_j2000, f[i].b_j2000, f[i].mag);
    }
}
 
void printCAT(cat_s *c, int catcount){
    int i=0;
    for(i=0;(i<(catcount-9));i++){
        printf("%lf\t %lf\t %lf\t %lf\t %lf\t %lf\t %lf\t %lf\t %lf\n", c[i].num, c[i].x, c[i].y, c[i].xworld, c[i].yworld, c[i].flux_auto, c[i].mag_auto, c[i].awin, c[i].bwin);
    }
}
 
int main(void) {
 
    float exptime = 0;
    float F = 0;
    float Mi = 0;
    float Mcat = 0;
    float FLUX_AUTO = 0;
    float ZP = 0;
    char fcatname[50];
    char catname[50];
    int fcatcount = 0;
    int catcount = 0;
    char fcat_c;
    char cat_c;
    fcat_s *f;
    cat_s *c;
 
    printf("Please input the .fcat file name:\n");
    scanf("%str", fcatname);
     
    printf("Please input the .cat file name:\n");
    scanf("%str", catname);
 
    printf("Please input the exposure time:\n");
    scanf("%f", &exptime);
     
    FILE* fcat;
    fcat = fopen(fcatname, "r");
 
    if (fcat == NULL) {
        printf("The input file does not exist\n");
    }
    else {
        for (fcat_c = getc(fcat); fcat_c != EOF; fcat_c = getc(fcat)){
                if (fcat_c == '\n')
                    fcatcount++;
        }
    }
     
    FILE* cat;
       cat = fopen(catname, "r");
     
    if (cat == NULL) {
        printf("The input file does not exist\n");
    }
    else {
        for (cat_c = getc(cat); cat_c != EOF; cat_c = getc(cat)) {
                if (cat_c == '\n')
                    catcount++;
          //  printf("%c", cat_c);
        }
    }
     
    printf("\n");
    printf("The .fcat file has %d lines. \n", fcatcount);
    printf("The .cat file has %d lines. \n", catcount);
     
    printf("\n\n");
     
    f = (fcat_s*)malloc(fcatcount*sizeof(fcat_s));
    c = (cat_s*)malloc(catcount*sizeof(cat_s));
     
    rewind(fcat);
    rewind(cat);
     
    readFCAT(fcat, f, fcatcount);
    readCAT(cat, c, catcount);
     
    printf("FCAT CONTENTS\n");
    printFCAT(f, fcatcount);
     
    printf("\n\n");
     
    printf("CAT CONTENTS\n");
    printCAT(c, catcount);
     
    fclose(fcat);
    fclose(cat);
     
 
    return 0;
}
  • Where in your code are you trying to skip the first few lines? If you refer to `for (i=0;i<(fcatcount);i++){ if (i>5){` - that doesn't modify the file stream at all – UnholySheep Jul 28 '20 at 22:41
  • @UnholySheep yeah that's what I was using to skip, I'm not sure what else to do. – halfgracesxx Jul 28 '20 at 22:43
  • @UnholySheep for that part it does, but the problem more is the fact that I keep getting zeros output when I read the file. I.e. when using the readFCAT and readCAT and trying to print them out. I can read the file if I use fgets, but I need it to be read as floats so I can do math. – halfgracesxx Jul 28 '20 at 22:48
  • this is the output The .fcat file has 214 lines. The .cat file has 1874 lines. FCAT CONTENTS 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 etc. – halfgracesxx Jul 28 '20 at 22:54

1 Answers1

0

Your link txt file points to a jpg file :(. It would be far more useful had you posted just a few actual lines from the files, here, in text mode. So we could copy it to, well, a text file.

Back to the program

  • You could use fgets() and feof() to count the lines instead of calling fgetc() so each call return just one line instead of one char to be then compared with a newline
  • since you read one file at a time may be you could use just one FILE*
  • for(int i =0;i<5;i+=1) fgets(buffer, 80, fcat); would skip the 5 lines. But you should skip them still in main() and call the read functions pointing to the first data points already.
  • in main() you should have declared f as fcat_s** and c as cat_s** in order to use it as an array in the functions, as in the pair argc/argv in C
  • in readFCAT() and possibly in readCAT() you do not need to pass the line count: you already used that to allocate the correct number of f and c structs so you can read until the end of file and it will be ok
  • fscanf() return a value and you should use it. The code in the functions could be like this:
    int _readFCAT( FILE* fcat, fcat_s* f)
    {   int i = 0;
        while (!feof(fcat)
        {
            int n = fscanf(
                fcat, "%f %f %f %f %f",
                f[i]->x,
                f[i]->y),
                f[i]->a_j2000,
                f[i]->b_j2000,
                f[i]->mag
                );
        if (n != 5) return -1; // should read every time 5
        i += 1; // next item
        };
        return 0;
    };

you can use a single fscanf() call to consume a line instead of one per field

  • A common alternate solution would be to use Fcat_data* readFCAT(char* FileName); and a struct like this

typedef struct
{
    float x;
    float y;
    float a_j2000;
    float b_j2000;
    float mag;
}   Fcat_s;

typedef struct
{
    unsigned count;
    Fcat_s** f;
}   Fcat_data;

Fcat_data* readFCAT(char*);

just like every C program does with the pair argc/argv . In this way you let readFCAT() do their stuff and return a populated Fcat_data struct with an array f of pointers for the count elements read from the file. Encapsulating this will give you more freedom in changing things in the future.

Since I have time today I will add an

Example: Consider this file

1
2
3
4
5
44752.008   3317.1910   231.943325247   -3.856654423   11.54381
4476        3317.1910   231.943325247   -3.856654423   11.54382
4476.3762   3317.1910   231.943325247   -3.856654423   11.54383
4476.3756   3317.1910   231.943325247   -3.856654423   11.54385
4476.3762   3317.1910   231.943325247   -3.856654423   11.54386

And this output


        5 sets of values

44752.007813    3317.190918     231.943329      -3.856654       11.543810
4476.000000     3317.190918     231.943329      -3.856654       11.543820
4476.375977     3317.190918     231.943329      -3.856654       11.543830
4476.375488     3317.190918     231.943329      -3.856654       11.543850
4476.375977     3317.190918     231.943329      -3.856654       11.543860

        End of data

From this program

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

typedef struct
{
    float x;
    float y;
    float a_j2000;
    float b_j2000;
    float mag;
}   FCAT_s;

typedef struct
{
    unsigned count;
    FCAT_s** f;
}   FCAT_data;

FCAT_data*  freeFCAT(FCAT_data*); // clear data
unsigned    line_count(char*); 
int         printFCAT(FCAT_data*); // show
FCAT_data*  readFCAT(char*); // build struct

int main(int argc,char** argv)
{
    char fName[50];
    const char* dflt = "fcat.txt";
    strcpy(fName, dflt);
    if (argc > 1)
        strcpy(fName, argv[1]); // if provided, uses the command line argument
    FCAT_data* f_db = readFCAT(fName);
    if (f_db == NULL) return -1;
    printFCAT(f_db);
    f_db = freeFCAT(f_db);
    return 0;
};

FCAT_data* freeFCAT(FCAT_data* db)
{
    // clean up
    for (int i = 0; i < db->count; i += 1)
        free(db->f[i]); // free item i
    free (db); // free db
    return NULL; // to invalidate pointer
};

unsigned    line_count(char* file)
{   // returns the # of lines in f
    FILE* f;
    char buffer[80];
    f = fopen(file, "r");
    if (f == NULL) return 0; // could not open
    int l = 0;
    while (!feof(f))
    {   fgets(buffer, 80, f);
        l += 1;
    };
    return l;
};

int         printFCAT(FCAT_data* data)
{
    // just dumps data on the screen
    printf("\n\t%d sets of values\n\n", data->count);
    for (unsigned i = 0; i < data->count; i += 1)
        printf( "%lf\t%lf\t%lf\t%lf\t%lf\n",
            data->f[i]->x,
            data->f[i]->y,
            data->f[i]->a_j2000,
            data->f[i]->b_j2000,
            data->f[i]->mag);
    printf("\n\tEnd of data\n\n");
    return 0;
};

FCAT_data*  readFCAT(char* file_name)
{
    unsigned l = line_count(file_name);
    if (l < 6)
    {
        printf("No data in '%s'\n", file_name);
        return NULL;
    };
    FILE* in = fopen(file_name, "r");
    char buffer[50];
    if (in == NULL) return NULL; // ? could not open
    for (int i = 0; i < 5; i += 1) fgets(buffer, 50, in);
    // now file points to the start of the data points
    // tme to create the databas struct
    FCAT_data* db = (FCAT_data*)malloc(sizeof(FCAT_data));
    db->count = 0;
    db->f = (FCAT_s**)malloc((l - 5) * sizeof(FCAT_s*));
    // database allocated with space for all datapoints
    while (!feof(in))
    {
        FCAT_s* next = malloc(sizeof(FCAT_s));
        int n = fscanf(
            in, "%f %f %f %f %f",
            &next->x,
            &next->y,
            &next->a_j2000,
            &next->b_j2000,
            &next->mag
        );
        if (n != 5)
        {   // last line could have just a newline?
            fclose(in);
            return db;
        };
        db->f[db->count] = next;
        db->count += 1;
    };
    fclose(in);
    return db;
};

You may find this way easier. You can pass the file name on the command line or use the default "fcat.txt"

4 functions in the example

FCAT_data*  freeFCAT(FCAT_data*); // clear data
unsigned    line_count(char*); 
int         printFCAT(FCAT_data*); // show
FCAT_data*  readFCAT(char*); // build struct

Not much to say: the file name is provided on the command line. You could pass 2 file names plus the exposure time. One functions builds the database from the file provided and return the address of the data. Other dumps the data to the screen for testing, other frees up the data, other count the lines in a file.

main()

int main(int argc,char** argv)
{
    char fName[50];
    const char* dflt = "fcat.txt";
    strcpy(fName, dflt);
    if (argc > 1)
        strcpy(fName, argv[1]); // if provided, uses the command line argument
    FCAT_data* f_db = readFCAT(fName);
    if (f_db == NULL) return -1;
    printFCAT(f_db);
    f_db = freeFCAT(f_db);
    return 0;
};

The minimum. Just an example. builds the database from file, shows data on screen, free memory and exit.

arfneto
  • 1,227
  • 1
  • 6
  • 13