由于结构能保存多种多样的信息,所以它是建立数据库的重要工具。一个结构中保存的整套信息用术语来说就是一个记录,单个的项目称为字段。
或许最显而易见也是最没效率的保存方法就是使用fprintf()。
一个更好的解决方法是使用fread( )和fwrite( )函数以结构的大小为单元进行读写。这些函数在读写时使用了与程序所使用的相同的二进制表示法。例如:
fwrite(&primer,sizeof(struct book),1,pbooks);
这个结构定位到结构primer的开始地址,将该结构的所有字节复制到与pbooks相关联的文件中。sizeof()告诉函数要复制的每一块有多大,1表示只需要复制1块。具有同样参数的fread()函数将把一个结构大小的数据从文件复制到&primer指向的位置。简单地说,这些函数一次性读写整个记录,而不是一个字段。
14.8.1 一个结构保存的实例
我们改写程序清单14.2中的程序,把书名保存到一个名为book.bat的文件中。如果该文件存在,程序显示文件当前内容,然后允许您向文件中添加内容。程序清单14.14即是新版本。
/*booksave.c --把结构内容保存到文件中*/
#include <stdio.h>
#include <stdlib.h>
#define MAXTITL 40
#define MAXAUTL 40
#define MAXBKS 10
struct book {
char title[MAXTITL];
char author[MAXAUTL];
float value;
};
int main(void)
{
struct book library[MAXBKS]; /*结构数组*/
int count = 0;
int index, filecount;
FILE *pbooks;
int size = sizeof(struct book);
if((pbooks=fopen("book.bat","a+b"))==NULL)
{
fputs("Can't open book.bat file \n",stderr);
exit(1);
}
rewind(pbooks); /*定位到文件开始处*/
while(count<MAXBKS && fread(&library[count],size,1,pbooks)==1)
{
if(count==0)
puts("Current contents of book.dat: ");
printf("%s by %s :$%.2f\n",library[count].title,library[count].author,library[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(library[count].title != NULL && library[count].title[0]!='\0'))
{
puts("Now enter the author.");
gets(library[count].author);
puts("Now enter the value");
scanf("%f",&library[count++].value);
while(getchar()!='\n')
continue;
if(count<MAXBKS)
puts("Enter the next title.");
}
if(count > 0)
{
puts("Here is the list of your books: ");
for(index=0;index<count;index++)
printf("%s by %s: $%.2f\n",library[count].title,library[count].author,library[count].value);
fwrite(&library[filecount],size,count-filecount,pbooks);
}
else
puts("No books? Too bad.\n");
puts("Bye.\n");
fclose(pbooks);
return 0;
}
14.8.2 程序要点
首先,使用“a+b"模式打开文件。a+部分允许程序读入整个文件,并向文件末尾添加数据。b是ANSI表示程序要使用二进制文件格式的方法。
我们选择二进制模式是因为fread()和fwrite()要使用二进制文件。的确,结构中有些内容是文本,但value成员不是文本,如果使用文本编辑器来查看book.dat,文本部分会正确显示,但是数字部分不可读,甚至还可能导致文本编辑器显示乱码。
命令rewind()确保文件指针处于文件开始位置,为开始读取做好准备。
最开始的那个while循环每次把一个结构读到结构数组中,当数组满或文件读完时停止。变量filecount用来保存已读结构的数目。
接下来的while循环提示并获取用户输入。当数组 满或用户在一行开始就按下enter,退出循环。注意,开始循环时,变量count具有前面那个循环之后的值。这将把新的输入项添加到数组 的末尾。
然后,for循环打印来自文件和来自用户的数据。因为文件是以追加模式打开的,所以将把新写入的内容追加到已有内容的后面。
我们本来可以使用一个循环每次把一个结构添加到文件末尾。但是我们决定使用fwrite()一次写入多个块的功能。表达式count-filecount得出要加入的新书的数目,函数调用fwrite()把这么多数目的book结构大小的块写入到文件中。表达式library[filecount]是数组中第一个新输入的结构的地址,因此复制就从这一点开始。
这个例子或许就是将结构写入文件和取回结构的最简单的方法。但是它浪费了空间,因为结构中没使用的部分也被保存了。这个结构的大小是2*40*sizeof(char)+sizeof(float),在我们的系统中占84字节,事实上,不是每一个输入都需要这么多的空间。但是每个数据块具有同样大小,会使取回数据时更加容易。