预计阅读本页时间:-
由于结构可以保存多种多样的信息,所以它是建立数据库的重要工具。例如,可以使用一个结构来保存有关一个雇员或汽车零件的所有相关信息。最终,您会希望把这些信息保存在一个文件中,并能在以后从文件中取回它们。一个数据库文件能够包含任意数目的此类数据对象。一个结构中保存的整套信息用术语来称就是一个记录(record),单个的项目称为字段(field)。我们来探讨这些问题。
或许最显而易见的也是最没效率的保存记录的方法就是使用fprintf()。例如,回忆一下程序清单14.1中介绍的结构book:
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
如果pbooks代表一个文件流,您可以用下列语句保存名为primer的struct book变量中的信息:
对于一些结构(比如有30个成员的结构),这个方法用起来很不方便。另外,在取回数据时它还存在问题,因为程序需要知道一个字段结束、另一个字段开始的位置。使用固定大小宽度的格式可以解决这个问题,例如“%39s%39s%8.2f”,但这个方法仍然很笨拙。
一个更好的解决方法是使用fread()和fwrite()函数以结构的大小为单元进行读写。回忆一下,这些函数在读写时使用了与程序所使用的相同的二进制表示法。例如:
这个语句定位到结构primer的开始地址,将该结构的所有字节复制到与pbooks相关联的文件中。sizeof(struct book)告诉函数要复制的每一块有多大,1表示只需要复制一块。具有同样参数的fread()函数将把一个结构大小的一块数据从文件复制到&primer指向的位置。简单地说,这些函数一次性读写整个记录,而不是一个字段。
14.8.1 一个结构保存的实例
为了说明在程序中如何使用这些函数,我们改写了程序清单14.2中的程序,把书名保存到一个名为book.dat的文件中。如果该文件已经存在,程序显示文件当前内容,然后允许您向文件中添加内容。程序清单14.14即是新版本(如果您正在使用早期的Borland编译器,请注意清单14.2附近的“Borland C和浮点数”部分的讨论)。
程序清单14.14 booksave.c程序
我们首先看看几个运行示例,然后讨论主要的编程要点。
再次运行程序booksave会把所有这3本书作为当前文件记录显示出来。
14.8.2 程序要点
首先,使用“a+b”模式打开文件。a+部分允许程序读入整个文件,并向文件末尾添加数据。b是ANSI表示程序要使用二进制文件格式的方法。对不接受b的UNIX系统来说,可以省略b,因为UNIX总共只有一种文件形式。对其他的ANSI之前的实现,您可能需要找到b的等价表示法。
我们选择二进制模式是因为fread()和fwrite()要使用二进制文件。的确,结构中有些内容是文本,但value成员不是文本。如果使用文本编辑器来查看book.dat,文本部分会正确显示,但是数字部分不可读,甚至还可能导致文本编辑器显示乱码。
命令rewind()确保文件位置指针处于文件开始部分,为开始读取做好准备。
最开始的那个while循环每次把一个结构读到结构数组中,当数组满或文件读完时停止。变量filecount用来保存己读结构的数目。
接下来的while循环提示并获取用户输入。像程序清单14.2所示那样,当数组满或用户在一行开始就按下回车键时,退出循环。注意,开始循环时,变量count具有前面那个循环之后的值。这将把新的输入项添加到数组的末尾。
然后,for循环打印来自文件和来自用户的数据。因为文件是以追加模式打开的,所以将把新写入的内容追加到已有内容后面。
我们本来可以使用一个循环来每次把一个结构添加到文件末尾。但是,我们决定使用fwrite()一次写入多个块的功能。表达式count-filecount得出要加入的新书的数目,函数调用fwrite()把这么多数目的book结构大小的块写入到文件中。表达式&library[filecount]是数组中第一个新输入的结构的地址,因此复制就从这一点开始。
这个例子或许就是将结构写入文件和取回结构的最简单的方法,但是它浪费了空间,因为结构中没使用的部分也被保存了。这个结构的大小是2×40×sizeof(char)+sizeof(float),在我们的系统中总共占84字节。事实上,不是每一个输入项都需要这么多空间。但是每个数据块具有同样大小会使取回数据时更容易。
另一种方法是使用不定大小的记录。为了便于从文件中读出这样的记录,每个记录可以用一个数值字段开始,这个字段用来指明该记录的大小。这种方法比我们刚才使用的方法要稍微复杂一些。通常,这种方法涉及到我们接下来要讲到的“链接结构”以及第16章“C预处理器和C库”要讨论的动态存储分配。