C语言:单链表储存以二进制形式储存数据到文件当中

382 阅读7分钟
编译工具:vc6.0


由于网上关于单链表如何储存数据到文件中的简单看的懂的例子很少,于是我近期就写了个较为简单的单链表储存数据进文件并进行读取的c代码。虽说简单,但代码量还是相当的长的,希望各位能耐心看,保证看完后会懂,如果看不懂,留言给我,保证回复。好,接下来上代码(小伙伴们可以先看整个的代码,如果看的懂的话恭喜你学会了,看不懂没关系,在后面我会详细的讲解)


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Imformation
{
	char name[10];
	int birth;
	struct Imformation *next;
};

struct Imformation2
{
	char name[10];
	int birth;
};

int main(void)
{
	FILE *fp;
	int flag=1,sign;
	struct Imformation *imt,*next,*last,*imt2,*q;
	struct Imformation2 *imt_for_write,*imt_for_read;

	imt=(struct Imformation*)malloc(sizeof(struct Imformation));
	imt2=(struct Imformation*)malloc(sizeof(struct Imformation));
	imt_for_write=(struct Imformation2 *)malloc(sizeof(struct Imformation2));
	imt_for_read=(struct Imformation2 *)malloc(sizeof(struct Imformation2));

	next=imt;
	while(flag==1)
	{
		printf("请输入姓名:");
		scanf("%s",next->name);
		printf("请输入年龄:");
		scanf("%d",&next->birth);
		printf("是否继续输入(是输入1):");
		scanf("%d",&sign);
		if(sign!=1)
		{
			flag=0;
			next->next=NULL;
		}
		else
			next->next=(struct Imformation*)malloc(sizeof(struct Imformation));
			next=next->next;
			
	}

	next=imt;
	if((fp=fopen("text.txt","w"))==NULL)
	{
		printf("文件打开失败");
		exit(1);
	}
	while(next!=NULL)
	{
		imt_for_write->birth=next->birth;
		strcpy(imt_for_write->name,next->name);
		fwrite(imt_for_write, sizeof(struct Imformation2), 1, fp);
		next=next->next;
	}
	fclose(fp);

	next=imt2;

	if((fp=fopen("text.txt","r"))==NULL)
	{
		printf("文件打开失败");
		exit(1);
	}
	while(1)
	{
		
		fread(imt_for_read, sizeof(struct Imformation2), 1, fp);
		next->birth=imt_for_read->birth;
		strcpy(next->name,imt_for_read->name);
		if (feof(fp))
                {
			free(next);
			last->next=NULL;
                         break;
                }
		next->next=(struct Imformation*)malloc(sizeof(struct Imformation));
		last=next;
		next=next->next;
	}
	next=imt2;

	while(next!=NULL)
	{
		printf("姓名:%s\n",next->name);
		printf("生日:%d\n",next->birth);
		next=next->next;
	}
	fclose(fp);
        next = imt;
	while(next !=NULL)
	{
		q=next->next;
		free(next);
		next=q;
	}

	next = imt2;
	while(next !=NULL)
	{
		q=next->next;
		free(next);
		next=q;
	}
	imt2=NULL;

	free(imt_for_write);
	free(imt_for_read);

        return 0;
}




好,代码比较长,我们分开成几部分来讲:

(1)首先第一部分,结构体的声明,在代码中,我们声明了两个结构体,如下


struct Imformation
{
	char name[10];
	int birth;
	struct Imformation *next;
};

struct Imformation2
{
	char name[10];
	int birth;
};

两个结构体除了名字不同外,还有的不同就是第一个结构体多了struct Imformation *next;这句话,表示定义了一个指向它本身的一个指针,形成链表,单链表,第二个只是一个简单的结构体,好,他们的作用是什么?

第一个带指针的结构体的作用分为两个阶段,第一个阶段,给数据,给里面的name,birth赋值,再给里面的next里的name,birth赋值,最后的表的next值为NULL;第二个阶段,读取文件后把数据写入这个单链表中。

第二个不带指针的结构体,作用也分为两个阶段,第一个阶段,把单链表的数据写入进这个结构体(不写入next,也就是不写入指针域),然后用这个结构体指针来写入文件;第二阶段,和第一阶段反过来,先把文件数据写入这个结构体2,然后定义一个单链表,把这个结构体2的数据写入单链表(多组数据循环写入,单组数据一次做完)

上面两句话的作用看不懂没关系,我们看代码

定义结构体


第一个结构体定义5个(p用作释放内存)

struct Imformation *imt,*next,*last,*imt2,*q;


第二个结构体定义两个


struct Imformation2 *imt_for_write,*imt_for_read;

然后,给他们分配内存(属于哪个结构体就分配哪个结构体的内存大小)


        imt=(struct Imformation*)malloc(sizeof(struct Imformation));
	imt2=(struct Imformation*)malloc(sizeof(struct Imformation));
	imt_for_write=(struct Imformation2 *)malloc(sizeof(struct Imformation2));
	imt_for_read=(struct Imformation2 *)malloc(sizeof(struct Imformation2));

这里我说一下为什么next和last不用分配内存,因为到下面他们会被已分配内存的指针赋值,所以不用分配内存(下面有解释例子)。

接下来,把next的值赋值给imt(解释例子1:因为已经分配了内存给imt,把imt的值赋给next,next就有了内存),


next=imt;

然后写入数据进链表,


while(flag==1)
	{
		printf("请输入姓名:");
		scanf("%s",next->name);
		printf("请输入年龄:");
		scanf("%d",&next->birth);
		printf("是否继续输入(是就输入1,否就输入其他数字):");
		scanf("%d",&sign);
		if(sign!=1)
		{
			flag=0;
			next->next=NULL;
		}
		else
			next->next=(struct Imformation*)malloc(sizeof(struct Imformation));
			next=next->next;
			
	}

当链表的表的个数大于等于两个的时候,

第一次循环:next==imt,next->next==imt->next,当要输入第二个表的时候,因为我们只给了第一个表的地址分配了内存,也就是给imt分配了内存(相当于给第一次循环的next分配了内存),但是,链接下一个表的指针,也就是next->next没有分配内存(相当于imt->next)没有分配内存,所以需要给他们分配内存,于是代码中就有了

next->next=(struct Imformation*)malloc(sizeof(struct Imformation));

这句代码就给下一个指针分配了内存,如果不分配内存,那么下一个指针的name值和birth值无法赋值,调试时会显示访问了不该访问的地址的英文。


第二次循环:同理,因为我们只给了第一个表,第二个表给了内存,第三个表没有分配内存,所以也要用到next->next=(struct Imformation*)malloc(sizeof(struct Imformation));这句代码分配内存。

当不需要继续输入信息时,就进入if(sign!=1)里面,不会执行到else的分配地址的代码(也就是上面说的next->next=(struct Imformation*)malloc(sizeof(struct Imformation));这串代码),而是把next->next的值赋值为NULL,表示链表结束。



(2)定义文件指针,


FILE *fp;

然后把链表的首地址赋给next,链表的首地址就是开始的imt


next=imt;


然后我们用fopen以写入的方式(w)打开文件,并判断是否打开失败,如下

if((fp=fopen("text.txt","w"))==NULL)
	{
		printf("文件打开失败");
		exit(1);
	}


然后,我们用下面这串代码,

while(next!=NULL)
	{
		imt_for_write->birth=next->birth;
		strcpy(imt_for_write->name,next->name);
		fwrite(imt_for_write, sizeof(struct Imformation2), 1, fp);
		next=next->next;
	}


这串代码的意思是,写一个循环,

把第一个表的数据写入imt_for_write,用fwrite把imt_for_write的数据写入文件中,然后用

next=next->next;指向下一个表的指针,如果为NULL,则退出循环,如果不是,则继续把下一个表的数据写入imt_for_write,用fwrite把imt_for_write的数据写入文件中,知道判断为NULL循环结束,这样单链表的数据就全部写入文件当中,单链表的指针域不会写入到文件当中(指针域写入是没用的)。

最后用fclose(fp);关闭文件。

这样,数据的储存就完毕了,数据已经存入文件当中了。


(3)下面是读文件的阶段

用 next=imt2; 这串代码把imt2的值赋给next


然后以只读方式(r)打开代码并判断打开是否失败

if((fp=fopen("text.txt","r"))==NULL)
	{
		printf("文件打开失败");
		exit(1);
	}

然后接下来的循环时读取文件的重点


        while(1)
	{
		
		fread(imt_for_read, sizeof(struct Imformation2), 1, fp);
		next->birth=imt_for_read->birth;
		strcpy(next->name,imt_for_read->name);
		if (feof(fp))
                {
			last->next=NULL;
                        break;
                }
		else
		{
			next->next=(struct Imformation*)malloc(sizeof(struct Imformation));
			last=next;
			next=next->next;
		}
	}


这块代码的意思是,写一个循环,把文件中的第一个表的信息写进imt_for_read里面,然后再把imt_for_read的值写入一个新的单链表内(也就是imt2这个单链表),然后用下面这串代码


                if (feof(fp))
                {
			last->next=NULL;
                        break;
                }
		else
		{
			next->next=(struct Imformation*)malloc(sizeof(struct Imformation));
			last=next;
			next=next->next;
		}
	}

来判断文件是否读到尾部了,如果没有给next->next分配一个内存,把next的值赋给last也就是把当前这个表的指针赋值给last,把next->next赋值给next也就是下一个表的指针赋值给next,然后再一次循环,把下一个表的值写入,如果判断为读到了尾部,则把last->next赋值为NULL,表示链表结束,退出循环。

最后输出imt2的数据,代码如下


        while(next!=NULL)
	{
		printf("姓名:%s\n",next->name);
		printf("生日:%d\n",next->birth);
		next=next->next;
	}

最后不要忘记用fclose把fp关闭指针以及释放内存。


谢谢大家!