简体   繁体   中英

Copy file into array(structure) using fscanf c

I'm trying to write a function that will copy floats from a file using fscanf and put them into an array. I also need to return the number of floats in the file. Here's what I currently have:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define MAX 100


struct cg {
    // structure to hold x and y co-ordinates and mass
    float x, y, mass;
}masses[MAX];


int readin(void)
{
    FILE *fp;
    masses cg;
//Error saying cg is an undeclared identifier.
    fp = fopen("m.txt", "rb");
    int n = 0;

    if (fp == NULL) {
        printf("Cannot find file.");
        return 0;
    }
    else {
        fscanf(fp, "%f" &cg.x, &cg.y, &cg.mass);
//Error here too
            n++;
            fclose(fp);
            getchar();
            return n;

    }

    }
    /* Write this function to read in the data from a file */
    /* into the array masses */
    /* note that this function should return the number of */
    /* masses read in from the file */


void computecg(int n_masses)
{
    /* Write this function to compute the C of G */
    /* and print the result */
}
void main(void)
{
    int number;
    if ((number = readin()) > 0) {
        computecg(number);
    }
}

This is my first year learning c and my lecturer is terrible, and help is much appreciated!

Define your structure cg and declare both the struct cg variables cg and masses as follow: while (n < MAX){ fscanf("%f %f %f", &cg.x, &cg.y, &cg.mass); masses[n] = cg; ++n;} return n; while (n < MAX){ fscanf("%f %f %f", &cg.x, &cg.y, &cg.mass); masses[n] = cg; ++n;} return n;

struct cg {
    float x, y, mass;
}cg, masses[MAX];

Then, in the function readin , you should comment the line containing masses cg and in the else block , you can write a while block like the following:

while (n < MAX){
    fscanf("%f %f %f", &cg.x, &cg.y, &cg.mass);
    masses[n] = cg;
    ++n;
}
return n;

There are a number of areas where you can "adjust" things to make your read of the positions and masses a bit more reliable. However, before looking at any of the specifics, let's begin with:

void main()

The proper declarations for main are int main (void) and int main (int argc, char **argv) (which you will see written with the equivalent char *argv[] ). note: main is a function of type int and it returns a value. See: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570) . See also: See What should main() return in C and C++?

Now let's talk about specifics. You define a struct and declare a global array of struct ( MAX of them) called masses with:

struct cg {
    // structure to hold x and y co-ordinates and mass
    float x, y, mass;
}masses[MAX];

( note: for any computation, you will want to change float to double to take advantage of the increased precision and to minimize rounding error.)

While a proper definition and declaration, avoid the use of global variables. They are unnecessary in all but a very few instances. Instead simply declare your struct cg and then declare your array in main() and pass the array as a parameter to each function that needs it. You have a global MAX properly defined, so all you need to pass to the function for filling is the array declared in main() (preferably along with an open file descriptor)

Why open the file in main() before calling readin() ? Obviously, if you attempt to open the file and it fails, there is little need to call readin() . So in general, open the file in the caller, validate the file is open, before passing the open FILE* stream pointer to the function for reading -- otherwise, there is no need to call the function. Eg

int main (int argc, char **argv)
{
    int number;
    struct cg masses[MAX] = {{ .x = 0.0 }};
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    if ((number = readin (masses, fp)) > 0) {
        printf ("\n%d objects read:\n\n", number);
        computecg (masses, number);
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

By doing it this way, your readin() reduces to:

int readin (struct cg *masses, FILE *fp)
{
    int n = 0;

    while (n < MAX && fscanf (fp, "%lf %lf %lf",
                        &masses[n].x, &masses[n].y, &masses[n].mass) == 3)
        n++;

    return n;
}

( note: how if (n < MAX && ... in your call to fscanf protects your array bounds by refusing to read more than MAX values. Always protect your array bounds anywhere the possibility exists your code could read more values than you have spaces for...)

Doing just what it was intended to do readin() the position and mass values from an open file and returning the number of object for which a position and mass was read.

You simply pass the filled array along with the number of object for which values are stored in your array. For example purposes, simply printing the contents in your masses array in the computecg() function, you could do:

void computecg (struct cg *masses, int n_masses)
{
    /* Write this function to compute the C of G */
    /* and print the result */
    for (int i = 0; i < n_masses; i++)
        printf ("%6.2f  %6.2f  %6.2f\n", 
                masses[i].x, masses[i].y, masses[i].mass);
}

Putting it altogether, the example would be:

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

#define MAX 100

struct cg {                 /* define struct, declare instance in main() */
    double x, y, mass;      /*  (a typedef will make things convenient)  */
};

int readin (struct cg *masses, FILE *fp)
{
    int n = 0;

    while (n < MAX && fscanf (fp, "%lf %lf %lf",
                        &masses[n].x, &masses[n].y, &masses[n].mass) == 3)
        n++;

    return n;
}

void computecg (struct cg *masses, int n_masses)
{
    /* Write this function to compute the C of G */
    /* and print the result */
    for (int i = 0; i < n_masses; i++)
        printf ("%6.2f  %6.2f  %6.2f\n", 
                masses[i].x, masses[i].y, masses[i].mass);
}

int main (int argc, char **argv)
{
    int number;
    struct cg masses[MAX] = {{ .x = 0.0 }};
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    if ((number = readin (masses, fp)) > 0) {
        printf ("\n%d objects read:\n\n", number);
        computecg (masses, number);
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */
#if defined (_WIN32) || defined (_WIN64)
    getchar();
#endif    
    return 0;
}

( note: the #if defined (_WIN32) || defined (_WIN64) is simply used to check if the program is being compiled on windows -- as there is no need to hold the terminal open with a getchar(); otherwise)

Example Input File

$ cat dat/cgmasses.txt
1.37 1.37 713.54
3.00 3.00 189.55
1.05 1.05 276.15
2.57 2.57 238.94
2.17 2.17 189.03
6.73 6.73 263.26
3.26 3.26 795.61
9.41 9.41 283.92
1.60 1.60 279.72
1.70 1.70 719.12

Example Use/Output

$ ./bin/cgmasses_read <dat/cgmasses.txt

10 objects read:

  1.37    1.37  713.54
  3.00    3.00  189.55
  1.05    1.05  276.15
  2.57    2.57  238.94
  2.17    2.17  189.03
  6.73    6.73  263.26
  3.26    3.26  795.61
  9.41    9.41  283.92
  1.60    1.60  279.72
  1.70    1.70  719.12

The computation of the cg is left to you. Look things over and let me know if you have further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM