龙空技术网

C语言如何把结构内容保存到文件中?

龙骑士洞察 240

前言:

眼前小伙伴们对“c语言数据结构下载”大约比较看重,各位老铁们都需要了解一些“c语言数据结构下载”的相关文章。那么小编同时在网上搜集了一些关于“c语言数据结构下载””的相关文章,希望你们能喜欢,咱们快快来了解一下吧!

可以用一个结构存储雇员或汽车零件的相关信息。最终,我们要把这些信息存储在文件中,并且能再次检索。数据库文件可以包含任意数量的此类数据对象。存储在一个结构中的整套信息被称为记录(record),单独的项被称为字段(field)。本节我们来探讨这个主题。 或许存储记录最没效率的方法是用fprintf()。例如,回忆程序清单14.1中的book结构:

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

如果pbook标识一个文件流,那么通过下面这条语句可以把信息存储在struct book类型的结构变量primer中:

fprintf(pbooks, "%s %s %.2fn", primer.title,        primer.author, primer.value);

对于一些结构(如,有30个成员的结构),这个方法用起来很不方便。另外,在检索时还存在问题,因为程序要知道一个字段结束和另一个字段开始的位置。虽然用固定字段宽度的格式可以解决这个问题(例如,"%39s%39s%8.2f"),但是这个方法仍然很笨拙。 更好的方案是使用fread()和fwrite()函数读写结构大小的单元。回忆一下,这两个函数使用与程序相同的二进制表示法。例如:

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

定位到primer结构变量开始的位置,并把结构中所有的字节都拷贝到与pbooks相关的文件中。sizeof(struct book)告诉函数待拷贝的一块数据的大小,1表明一次拷贝一块数据。带相同参数的fread()函数从文件中拷贝一块结构大小的数据到&primer指向的位置。简而言之,这两个函数一次读写整个记录,而不是一个字段。 以二进制表示法存储数据的缺点是,不同的系统可能使用不同的二进制表示法,所以数据文件可能不具可移植性。甚至同一个系统,不同编译器设置也可能导致不同的二进制布局。

1 保存结构的程序示例

为了演示如何在程序中使用这些函数,我们把程序清单14.2修改为一个新的版本(即程序清单14.14),把书名保存在book.dat文件中。如果该文件已存在,程序将显示它当前的内容,然后允许在文件中添加内容(如果你使用的是早期的Borland编译器,请参阅程序清单14.2后面的“Borland C和浮点数”)。

Listing 14 booksave.c

/* booksave.c -- saves structure contents in a file */#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAXTITL  40#define MAXAUTL  40#define MAXBKS   10             /* maximum number of books */char * s_gets(char * st, int n);struct book {                   /* set up book template    */    char title[MAXTITL];    char author[MAXAUTL];    float value;};int main(void){    struct book library[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 filen",stderr);        exit(1);    }​    rewind(pbooks);            /* go to start of file     */    while (count < MAXBKS &&  fread(&library[count], size,                                    1, pbooks) == 1)    {        if (count == 0)            puts("Current contents of book.dat:");        printf("%s by %s: $%.2fn",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 && s_gets(library[count].title, MAXTITL) != NULL           && library[count].title[0] != '0')    {        puts("Now enter the author.");        s_gets(library[count].author, MAXAUTL);        puts("Now enter the value.");        scanf("%f", &library[count++].value);        while (getchar() != 'n')            continue;                /* clear input line  */        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: $%.2fn",library[index].title,                   library[index].author, library[index].value);        fwrite(&library[filecount], size, count - filecount,               pbooks);    }    else        puts("No books? Too bad.n");​    puts("Bye.n");    fclose(pbooks);​    return 0;}​char * s_gets(char * st, int n){    char * ret_val;    char * find;​    ret_val = fgets(st, n, stdin);    if (ret_val)    {        find = strchr(st, 'n');   // look for newline        if (find)                  // if the address is not NULL,            *find = '0';          // place a null character there        else            while (getchar() != 'n')                continue;          // dispose of rest of line    }    return ret_val;}​

我们先看几个运行示例,然后再讨论程序中的要点。

$ 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: $18.99 Deadly Farce by Dudley Forse: $15.99 Bye. $ booksave Current contents of book.dat: Metric Merriment by Polly Poetica: $18.99 Deadly Farce by Dudley Forse: $15.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: $18.99 Deadly Farce by Dudley Forse: $15.99 The Third Jar by Nellie Nostrum: $22.99 Bye. $

再次运行booksave.c程序把这3本书作为当前的文件记录打印出来。

2 程序要点

首先,以"a+b"模式打开文件。a+部分允许程序读取整个文件并在文件的末尾添加内容。b是ANSI的一种标识方法,表明程序将使用二进制文件格式。对于不接受b模式的UNIX系统,可以省略b,因为UNIX只有一种文件形式。对于早期的ANSI实现,要找出和b等价的表示法。 我们选择二进制模式是因为fread()和fwrite()函数要使用二进制文件。虽然结构中有些内容是文本,但是value成员不是文本。如果使用文本编辑器查看book.dat,该结构本文部分的内容显示正常,但是数值部分的内容不可读,甚至会导致文本编辑器出现乱码。 rewind()函数确保文件指针位于文件开始处,为读文件做好准备。

第1个while循环每次把一个结构读到结构数组中,当数组已满或读完文件时停止。变量filecount统计已读结构的数量。 第2个while循环提示用户进行输入,并接受用户的输入。和程序清单14.2一样,当数组已满或用户在一行的开始处按下Enter键时,循环结束。注意,该循环开始时count变量的值是第1个循环结束后的值。该循环把新输入项添加到数组的末尾。 然后for循环打印文件和用户输入的数据。因为该文件是以附加模式打开,所以新写入的内容添加到文件现有内容的末尾。 我们本可以用一个循环在文件末尾一次添加一个结构,但还是决定用fwrite()一次写入一块数据。对表达式count - filecount求值得新添加的书籍数量,然后调用fwrite()把结构大小的块写入文件。由于表达式&library[filecount]是数组中第1个新结构的地址,所以拷贝就从这里开始。 也许该例是把结构写入文件和检索它们的最简单的方法,但是这种方法浪费存储空间,因为这还保存了结构中未使用的部分。该结构的大小是2×40×sizeof(char)+sizeof(float),在我们的系统中共84字节。实际上不是每个输入项都需要这么多空间。但是,让每个输入块的大小相同在检索数据时很方便。 另一个方法是使用可变大小的记录。为了方便读取文件中的这种记录,每个记录以数值字段规定记录的大小。这比上一种方法复杂。通常,这种方法涉及接下来要介绍的“链式结构”和第12章的动态内存分配。

标签: #c语言数据结构下载