Saving the Structure Contents in a File

I l @ ve RuBoard

Saving the Structure Contents in a File

Because structures can hold a wide variety of information, they are an important tool for constructing databases. For example, you could use a structure to hold all the pertinent information about an employee or an auto part. Ultimately, you would want to be able to save this information in, and retrieve it from, a file. A database file could contain an arbitrary number of such data objects. The entire set of information held in a structure is termed a record , and the individual items are fields . Let's investigate these topics.

What is perhaps the most obvious way to save a record is the least efficient way, and that is to use fprintf() . For example, recall the book structure introduced in Listing 14.1:

 #define MAXTITL   40 #define MAXAUTL   40 struct book {     char title[MAXTITL];     char author[MAXAUTL];     float value; }; 

If pbooks identified a file stream, you could save the information in a struct book variable called primer with the following statement:

 fprintf(pbooks, "%s %s %.2f\n", primer.title,         primer.author,primer.value); 

This setup becomes unwieldy for structures with, say, 30 members . Also, it poses a retrieval problem because the program would need some way of telling where one field ends and another begins. This problem can be fixed by using a format with fixed- size fields, for example, "%39s%39s%8.2f" , but the awkwardness remains.

A better solution is to use fread() and fwrite() to read and write structure- sized units. Recall that these functions read and write using the same binary representation that the program uses. For example,

 fwrite(&primer, sizeof (struct book), 1, pbooks); 

goes to the beginning address of the primer structure and copies all the bytes of the structure to the file associated with pbooks . The sizeof (struct book) term tells the function how large a block to copy, and the 1 indicates that it should copy just one block. The fread() function with the same arguments copies a structure-sized chunk of data from the file to the location pointed to by &primer . In short, these functions read and write one whole record at a time instead of a field at a time.

To show how these functions can be used in a program, we've modified the program in Listing 14.2 so that the book titles are saved in a file called book.dat . If the file already exists, the program shows you its current contents and then enables you to add to the file. Listing 14.11 presents the new version. (If you're using an older Borland compiler, review the "Borland C and Floating Point" discussion in the box near Listing 14.2.)

Listing 14.11 The booksave.c program.
 /* booksave.c -- saves structure contents in a file */ #include <stdio.h> #include <stdlib.h> #define MAXTITL   40 #define MAXAUTL   40 #define MAXBKS   10             /* maximum number of books */ struct book {                   /* set up book template    */     char title[MAXTITL];     char author[MAXAUTL];     float value; }; int main(void) {      struct book libry[MAXBKS]; /* array of structures     */      int count = 0;      int index, filecount;      FILE * pbooks;      int size = sizeof (struct book);      if ((pbooks = fopen("book.dat", "a+b")) == NULL)      {          fputs("Can't open book.dat file\n",stderr);          exit(1);      }      rewind(pbooks);            /* go to start of file     */      while (count < MAXBKS &&  fread(&libry[count], size,                  1, pbooks) == 1)      {          if (count == 0)              puts("Current contents of book.dat:");          printf("%s by %s: $%.2f\n",libry[count].title,              libry[count].author, libry[count].value);          count++;      }      filecount = count;      if (count == MAXBKS)      {          fputs("The book.dat file is full.", stderr);          exit(2);      }      puts("Please add new book titles.");      puts("Press [enter] at the start of a line to stop.");      while (count < MAXBKS && gets(libry[count].title) != NULL                          && libry[count].title[0] != ' 
  /* booksave.c -- saves structure contents in a file */ #include <stdio.h> #include <stdlib.h> #define MAXTITL 40 #define MAXAUTL 40 #define MAXBKS 10 /* maximum number of books */ struct book { /* set up book template */ char title[MAXTITL]; char author[MAXAUTL]; float value; }; int main(void) { struct book libry[MAXBKS]; /* array of structures */ int count = 0; int index, filecount; FILE * pbooks; int size = sizeof (struct book); if ((pbooks = fopen("book.dat", "a+b")) == NULL) { fputs("Can't open book.dat file\n",stderr); exit(1); } rewind(pbooks); /* go to start of file */ while (count < MAXBKS && fread(&libry[count], size, 1, pbooks) == 1) { if (count == 0) puts("Current contents of book.dat:"); printf("%s by %s: $%.2f\n",libry[count].title, libry[count].author, libry[count].value); count++; } filecount = count; if (count == MAXBKS) { fputs("The book.dat file is full.", stderr); exit(2); } puts("Please add new book titles."); puts("Press [enter] at the start of a line to stop."); while (count < MAXBKS && gets(libry[count].title) != NULL && libry[count].title[0] != '\0') { puts("Now enter the author."); gets(libry[count].author); puts("Now enter the value."); scanf("%f", &libry[count++].value); while ( getchar () != '\n') continue; /* clear input line */ if (count < MAXBKS) puts("Enter the next title."); } puts("Here is the list of your books:"); for (index = 0; index < count; index++) printf("%s by %s: $%.2f\n",libry[index].title, libry[index].author, libry[index].value); fwrite(&libry[filecount], size, count - filecount, pbooks); fclose(pbooks); return 0; }  
') { puts("Now enter the author."); gets(libry[count].author); puts("Now enter the value."); scanf("%f", &libry[count++].value); while (getchar() != '\n') continue; /* clear input line */ if (count < MAXBKS) puts("Enter the next title."); } puts("Here is the list of your books:"); for (index = 0; index < count; index++) printf("%s by %s: $%.2f\n",libry[index].title, libry[index].author, libry[index].value); fwrite(&libry[filecount], size, count - filecount, pbooks); fclose(pbooks); return 0; }

We'll look at a couple of sample runs and then discuss the main programming points.

 %  booksave  Please add new book titles. Press [enter] at the start of a line to stop.  Metric Merriment  Now enter the author.  Polly Poetica  Now enter the value.  18.99  Enter the next title.  Deadly Farce  Now enter the author.  Dudley Forse  Now enter the value.  15.99  Enter the next title.  [enter]  Here is the list of your books: Metric Merriment by Polly Poetica: .99 Deadly Farce by Dudley Forse: .99 %  booksave  Current contents of book.dat: Metric Merriment by Polly Poetica: .99 Deadly Farce by Dudley Forse: .99 Please add new book titles.  The Third Jar  Now enter the author.  Nellie Nostrum  Now enter the value.  22.99  Enter the next title.  [enter]  Here is the list of your books: Metric Merriment by Polly Poetica: .99 Deadly Farce by Dudley Forse: .99 The Third Jar by Nellie Nostrum: .99 % 

Running the booksave program again would show all three books as current file records.

Program Points

First, the "a+b" mode is used for opening the file. The a+ part lets the program read the whole file and append data to the end of the file. The b is the ANSI way of signifying that the program will use the binary file format. For UNIX systems that don't accept the b , you can omit it because UNIX has only one file form, anyway. For other pre-ANSI implementations , you might need to find the local equivalent to using b .

We chose the binary mode because fread() and fwrite() are intended for binary files. True, some of the structure contents are text, but the .value member is not. If you use a text editor to look at book.dat , the text part will show up okay, but the numeric part will be unreadable and could even cause your text editor to barf.

The rewind() command ensures that the file position pointer is situated at the start of the file, ready for the first read.

The initial while loop reads one structure at a time into the array of structures, stopping when the array is full or when the file is exhausted. The variable filecount keeps track of how many structures were read.

The next while loop prompts for, and takes, user input. As in Listing 14.2, this loop quits when the array is full or when the user presses the Enter key at the beginning of a line. Notice that the count variable starts with the value it had after the preceding loop. This causes the new entries to be added to the end of the array.

The for loop then prints the data both from the file and from the user. Because the file was opened in the append mode, new writes to the file are appended to the existing contents.

We could have used a loop to add one structure at a time to the end of the file. However, we decided to use the ability of fwrite() to write more than one block at a time. The expression count - filecount yields the number of new book titles to be added, and the call to fwrite() writes that number of structure-sized blocks to the file. The expression &libry[filecount] is the address of the first new structure in the array, so copying begins from that point.

This example is, perhaps, the simplest way to write structures to a file and to retrieve them, but it can waste space because the unused parts of a structure are saved, too. The size of this structure is 2 x 40 x sizeof (char) + sizeof (float) , which totals 84 bytes on our system. None of the entries actually needed all that space. However, each data chunk being the same size makes retrieving the data easy.

Another approach is to use variably sized records. To facilitate reading such records from a file, each record can begin with a numerical field specifying the record size. This is a bit more complex than what we have done. Normally, this method involves "linked structures," which we describe next, and dynamic memory allocation, which we discuss in Chapter 16, "The C Preprocessor and the C Library."

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net