二进制文件
二进制文件与结构数组非常相似,不同之处在于结构位于磁盘文件中,而不是内存中的数组中。由于二进制文件中的结构位于磁盘上,因此您可以创建非常大的集合(仅受可用磁盘空间的限制)。它们也是永久性的且始终可用。唯一的缺点是磁盘访问时间带来的缓慢。
二进制文件具有两个与文本文件不同的特点:
广告
- 您可以立即跳转到文件中的任何结构,这提供了像数组一样的随机访问。
- 您可以随时更改文件中任何位置的结构内容。
二进制文件通常也比文本文件具有更快的读写时间,因为记录的二进制图像直接从内存存储到磁盘(反之亦然)。在文本文件中,所有内容都必须在文本之间来回转换,这需要时间。
C 语言非常清晰地支持“结构文件”的概念。一旦打开文件,您可以读取结构、写入结构或查找文件中的任何结构。这个文件概念支持**文件指针**的概念。当文件打开时,指针指向记录 0(文件中的第一个记录)。任何**读取操作**都会读取当前指向的结构并将指针向下移动一个结构。任何**写入操作**都会写入当前指向的结构并将指针向下移动一个结构。**查找**将指针移动到请求的记录。
请记住,C 语言将磁盘文件中的所有内容都视为从磁盘读入内存或从内存写入磁盘的字节块。C 语言使用文件指针,但它可以指向文件中的任何字节位置。因此,您必须跟踪这些信息。
以下程序说明了这些概念:
#include <stdio.h> /* random record description - could be anything */ struct rec { int x,y,z; }; /* writes and then reads 10 arbitrary records from the file "junk". */ int main() { int i,j; FILE *f; struct rec r; /* create the file of 10 records */ f=fopen("junk","w"); if (!f) return 1; for (i=1;i<=10; i++) { r.x=i; fwrite(&r,sizeof(struct rec),1,f); } fclose(f); /* read the 10 records */ f=fopen("junk","r"); if (!f) return 1; for (i=1;i<=10; i++) { fread(&r,sizeof(struct rec),1,f); printf("%d\n",r.x); } fclose(f); printf("\n"); /* use fseek to read the 10 records in reverse order */ f=fopen("junk","r"); if (!f) return 1; for (i=9; i>=0; i--) { fseek(f,sizeof(struct rec)*i,SEEK_SET); fread(&r,sizeof(struct rec),1,f); printf("%d\n",r.x); } fclose(f); printf("\n"); /* use fseek to read every other record */ f=fopen("junk","r"); if (!f) return 1; fseek(f,0,SEEK_SET); for (i=0;i<5; i++) { fread(&r,sizeof(struct rec),1,f); printf("%d\n",r.x); fseek(f,sizeof(struct rec),SEEK_CUR); } fclose(f); printf("\n"); /* use fseek to read 4th record, change it, and write it back */ f=fopen("junk","r+"); if (!f) return 1; fseek(f,sizeof(struct rec)*3,SEEK_SET); fread(&r,sizeof(struct rec),1,f); r.x=100; fseek(f,sizeof(struct rec)*3,SEEK_SET); fwrite(&r,sizeof(struct rec),1,f); fclose(f); printf("\n"); /* read the 10 records to insure 4th record was changed */ f=fopen("junk","r"); if (!f) return 1; for (i=1;i<=10; i++) { fread(&r,sizeof(struct rec),1,f); printf("%d\n",r.x); } fclose(f); return 0; }
在此程序中,使用了结构描述 **rec**,但您可以使用任何您想要的结构描述。您可以看到 **fopen** 和 **fclose** 的工作方式与它们用于文本文件时完全相同。
这里的新函数是 **fread**、**fwrite** 和 **fseek**。fread 函数接受四个参数:
- 内存地址
- 每个块读取的字节数
- 要读取的块数
- 文件变量
因此,行 **fread(&r,sizeof(struct rec),1,f);** 表示从文件 **f**(从文件指针的当前位置)读取 12 字节(**rec** 的大小)到内存地址 **&r**。请求一个 12 字节的块。通过将 1 改为 100,可以同样轻松地从磁盘读取 100 个块到内存中的数组。
**fwrite** 函数的工作方式相同,但将字节块从内存移动到文件。**fseek** 函数将文件指针移动到文件中的某个字节。通常,您会以 **sizeof(struct rec)** 的增量移动指针,以将指针保持在记录边界。查找时可以使用三个选项:
- SEEK_SET
- SEEK_CUR
- SEEK_END
**SEEK_SET** 将指针从文件开头(文件中的字节 0)向下移动 **x** 字节。**SEEK_CUR** 将指针从当前指针位置向下移动 **x** 字节。**SEEK_END** 将指针从文件末尾移动(因此您必须使用负偏移量)。
上述代码中出现了几种不同的选项。特别要注意文件以 **r+** 模式打开的部分。这会打开文件进行读写,从而允许更改记录。代码查找记录,读取它,并更改一个字段;然后因为它读取时指针移位了,所以又回溯并写回更改。