简体   繁体   中英

Reading unknown number of structs from file - C

I'm having a bit of trouble getting my program to read in data from a file. The issue is that the file is currently empty. Each time the program is run, a single array of books[] will be populated and written to the file later on in the code. While I'm sure it will work when all 10 structs are in the file, at the moment it's crashing since the file is empty and it's trying to read in 10 structs.

Is there a way to read in an unknown number of structs (up to 10) from the file?

struct stock
{
    char name[31];
    int stock;
};

int main (void)
{
    stock books[10];

    FILE *fptr;
    fptr = fopen("stock.dat", "rb");
    fread(books, sizeof(struct stock), 10, fptr);

    fclose (fptr);
}

Yes, you can do it:

  • You need to check the value returned by fopen to make sure the file exists
  • You need to check the number of items read - the size_t value returned by fread

If you know the maximum possible number of structures in the file and can afford to have them all in memory:

int main (void)
{
    #define MAX_BOOKS 10
    stock books[MAX_BOOKS];
    size_t cnt_books = 0;
    FILE *fptr;
    fptr = fopen("stock.dat", "rb");
    cnt_books = fread(books, sizeof(struct stock), MAX_BOOKS, fptr);
    fclose (fptr);
    return 0;
}

otherwise loop and read in chunks:

    while (cnt_books = fread(books, sizeof(struct stock), MAX_BOOKS, fptr)) {
      /* ... */
    }

Crashing? Not those statements, I hope, unless the file's not there at all. It may be crashing if you assume you have ten valid items in your array, since the name fields probably won't be valid C strings.

The way you figure out how many you actually read is with:

num = fread(books, sizeof(struct stock), 10, fptr);

although I would prefer:

num = fread (book, sizeof(*book), sizeof(book) / sizeof(*book), fptr);

since that means you don't have to change lots of code in the event the type name or array size changes.

If it's possible the the file doesn't even open, you need to check the fopen return value as well. Complete code would look something like:

#include <stdio.h>

typedef struct {
    char name[31];
    int stock;
} tStock;

int main (void) {
    tStock book[10];
    size_t num, i;

    FILE *fptr = fopen ("stock.dat", "rb");
    if (fptr == NULL) {
        num = 0;
    } else {
        num = fread (book, sizeof(*book), sizeof(book) / sizeof(*book), fptr);
        fclose (fptr);
    }

    printf ("Read %d items\n", num);
    for (i = 0; i < num; i++) {
        printf ("   Item %d is %s, %d\n", book[i].name, book[i].stock);
    }

    return 0;
}

The code looks fine (have not give it a go though). See fread man page - it returns the number of items read.

fptr = fopen("stock.dat", "rb");
if(fptr == NULL)
{
  // error
}
else
{
  for(int i=0; i<10 && !feof(fptr); i++)
  {
    fread(&books[i], sizeof(struct stock), 1, fptr);
  }
  fclose(fptr);
}

I'm not sure, but fread() is not supposed to crash, but to return the number of items read, which is 0 in this case. Also I don't exactly understand how the line stock books[10]; compiles, it should be struct stock books[10]; .

I would suggest two things: 1. replace to struct stock books[10]; 2. An important thing to do is check that fptr is not NULL. Perhaps it couldn't open the file (maybe not in same directory or doesn'e exist yet), which results in a NULL fptr, and using it in fread would crash the app.

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