type
status
date
slug
summary
tags
category
icon
password
Property
由于结构可以储存不同类型的信息,所以它是构建数据库的重要工具。
例如,可以用一个结构储存雇员或汽车零件的相关信息。最终,我们要把这些信息储存在文件中,并且能再次检索。数据库文件可以包含任意数量的此类数据对象。储存在一个结构中的整套信息被称为记录(record),单独的项被称为字段(field)。
储存记录最没效率的方法是用fprintf()
如果pbook标识一个文件流,那么通过下面这条语句可以把信息储存在struct book类型的结构变量primer中:
对于一些结构(如,有 30 个成员的结构),这个方法用起来很不方便。另外,在检索时还存在问题,因为程序要知道一个字段结束和另一个字段开始的位置。虽然用固定字段宽度的格式可以解决这个问题,但是这个方法仍然很笨拙。
更好的方案是使用fread()和fwrite()函数读写结构大小的单元,这两个函数使用与程序相同的二进制表示法。例如:
定位到 primer 结构变量开始的位置,并把结构中所有的字节都拷贝到与 pbooks 相关的文件中。sizeof(struct book)告诉函数待拷贝的一块数据的大小,1 表明一次拷贝一块数据。带相同参数的fread()函数从文件中拷贝一块结构大小的数据到&primer指向的位置。简而言之,这两个函数一次读写整个记录,而不是一个字段。
以二进制表示法储存数据的缺点是,不同的系统可能使用不同的二进制表示法,所以数据文件可能不具可移植性。甚至同一个系统,不同编译器设置也可能导致不同的二进制布局。
首先,以"a+b"模式打开文件。a+部分允许程序读取整个文件并在文件的末尾添加内容。b 是 ANSI的一种标识方法,表明程序将使用二进制文件格式。对于不接受b模式的UNIX系统,可以省略b,因为UNIX只有一种文件形式。对于早期的ANSI实现,要找出和b等价的表示法。
我们选择二进制模式是因为fread()和fwrite()函数要使用二进制文件。虽然结构中有些内容是文本,但是value成员不是文本。如果使用文本编辑器查看book.dat,该结构本文部分的内容显示正常,但是数值部分的内容不可读,甚至会导致文本编辑器出现乱码。
rewrite()函数确保文件指针位于文件开始处,为读文件做好准备。
第1个while循环每次把一个结构读到结构数组中,当数组已满或读完文件时停止。变量filecount统计已读结构的数量。
第2个while按下循环提示用户进行输入,并接受用户的输入。和程序一样,当数组已满或用户在一行的开始处按下Enter键时,循环结束。
注意,该循环开始时count变量的值是第1个循环结束后的值。该循环把新输入项添加到数组的末尾。
然后for循环打印文件和用户输入的数据。因为该文件是以附加模式打开,所以新写入的内容添加到文件现有内容的末尾。
我们本可以用一个循环在文件末尾一次添加一个结构,但还是决定用fwrite()一次写入一块数据。对表达式count - filecount求值得新添加的书籍数量,然后调用fwrite()把结构大小的块写入文件。由于表达式&library[filecount]是数组中第1个新结构的地址,所以拷贝就从这里开始。
也许该例是把结构写入文件和检索它们的最简单的方法,但是这种方法浪费存储空间,因为这还保存了结构中未使用的部分。该结构的大小是2×40×sizeof(char)+sizeof(float),在我们的系统中共84字节。实际上不是每个输入项都需要这么多空间。但是,让每个输入块的大小相同在检索数据时很方便。
另一个方法是使用可变大小的记录。为了方便读取文件中的这种记录,每个记录以数值字段规定记录的大小。这比上一种方法复杂。