链表与文件读写实例

12月份的第一篇博客,最近忙着吃鸡、忙着上班,写好的代码一直没有发布上来。

========================================================
本篇涉及到的知识点
1、链表的操作(建立、初始化、排序、遍历、查找)
2、c语言文件读写操作(fgets读取文本内容后去除’\n’)
fopen()第二个参数
在这里插入图片描述
3、C语言时间函数(具体请百度自学)

  • time_t t=time(NULL);//获取1990年开始到现在的计时

  • struct tm* local=localtime(&t);//上述时间转为人类方便查看的时间

4、随机数种子

  • srand((unsigned)time(NULL));
    ========================================================
    立意及说明
    1、不要简单的练习一个知识点,链表与文件操作合起来,效果也不错(本来想着mysql数据库一起加进来,debug失败了,后续再慢慢学习数据库)
    2、使用开发工具 VS2013
    3、结构体Node的成员name专门使用指针,为的就是练习内存分配与释放(但是我这里失败了)
    ========================================================
    贴代码
    宏定义&头文件
#define _CRT_SECURE_NO_WARNINGS	//关闭VS开发工具的安全检查
//#include<windows.h>
#include <stdio.h>
#include<string.h>
#include<time.h>

全局变量(位于全局区,也称static区)-----C语言内存四区,自己百度

char *wday[] = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
typedef struct Node{
	char* name;		//此处刻意使用指针
	char sex[4];	//性别
	int age;		
	char addr[500];//地址
	time_t hiredate;//入职日期
	struct Node* next;
}Node;

处理fgets()文件操作后的字符串问题

//自定义函数:去除字符串末尾的 '\n'
void getRid(char* str){
	str[strlen(str) - 1] = '\0';//fgets()得到的最后一个字符是\n,此处改为\0
}

//创建链表

Node* Create(){
	//头结点
	Node* head = (Node*)malloc(sizeof(Node));
	//分配空间成功?
	if (head == NULL){
		return NULL;
	}
	//非循环单链表的头结点的初始化
	head->name = NULL;
	head->age = 0;
	strcpy(head->sex,"");		//char* 与char []类型非初始化时不可使用等号赋值
	strncpy(head->addr,"",1);
	head->hiredate = NULL;

	head->next = NULL;
	return head;
}

//链表初始化数据


void Init(Node* head){
	//判空,保证是带头结点的单链表
	if (head == NULL)
		return;

	//一些局部变量,位于栈区
	int age;
	char name[20] = {0};
	char paddr[500] = {0};
	time_t t;
	struct tm* local = NULL;
	//随机数种子
	srand((unsigned)time(NULL));

	//打开名字文件,Node.name从此文件中获取
	FILE* fp1 = fopen("name.txt", "r");
	//打开地址文件  Node.addr从此文件中获取
	FILE* fp2 = fopen("addr.txt", "r");
	
	//链表初始6条数据
	for (int i = 0; i < 6; ++i){
		Node* newNode = (Node*)malloc(sizeof(Node));
		char* pName= (char*)malloc(sizeof(20));//允许名字长度为20
		newNode->name = pName;
		//名字
		fgets(name, 20, fp1);
		getRid(name);
		strcpy(newNode->name, name);
		//年龄
		newNode->age = rand() % 20 + 15;
		//性别
		strcpy(newNode->sex, "男");
		//地址
		fgets(paddr, 500, fp2);
		//printf("\n%s\n", paddr);
		getRid(paddr);
		strcpy(newNode->addr, paddr);
		
		//入职日期
		t = time(NULL);
		newNode->hiredate = t;

		newNode->next = NULL;

		//节点挂到链表上---头插法
		newNode->next = head->next;
		head->next = newNode;
		Sleep(300);
	}
	//关闭文件
	fclose(fp1);
	fclose(fp2);
}

//遍历链表


void Travse(Node* head){
	if (head == NULL)
		return;
	struct tm* local;
	//用于遍历的指针
	Node* pCur = head->next;
	while (pCur){		//写法相当于while(PCur!=NULL)
		printf("%-8s %-2d ",pCur->name,pCur->age);
		printf("%-4s ", pCur->sex);
		printf(" %-50s", pCur->addr);
		local = localtime(&pCur->hiredate);//转为本地时间格式
		printf("%d-%d-%d ", local->tm_year + 1900, local->tm_mon + 1, local->tm_mday);//年月日
		printf("%s %d:%d:%d\n",wday[local->tm_wday],local->tm_hour,local->tm_min,local->tm_sec);//周几 时分秒

		pCur = pCur->next;
	}
}

//排序链表–插入法排序(这一块我也有待认真学习,读者百度自学)


void Sort(Node* head){
	if (head == NULL){
		return;
	}
	//最少2个节点才排序
	if (head->next == NULL || head->next->next==NULL){
		return;
	}

	Node *p1, *prep1, *p2, *prep2, *temp;
	
	prep1 = head->next;
	p1 = prep1->next;
	//prep1和p1是否需要手动后移
	int flag;

	while (p1 != NULL)
	{
		flag = 1;
		temp = p1;
		//由于是单向链表,所以只能从头部开始检索
		for (prep2 = head, p2 = prep2->next; p2 != p1; prep2 = prep2->next, p2 = p2->next)
		{
			//发现第一个较大值
			if (p2->age > p1->age)
			{
				p1 = p1->next;
				prep1->next = p1;
				prep2->next = temp;
				temp->next = p2;
				flag = 0;
				break;
			}
		}
		//手动后移prep1和p1
		if (flag)
		{
			prep1 = prep1->next;
			p1 = p1->next;
		}
	}



}

//逆置链表–头插法(还有一种方法是就地逆置法,读者自己百度)

void Reverse(Node* head){
	if (head == NULL){
		return;
	}
	//最少2个节点才逆置
	if (head->next == NULL || head->next->next == NULL){
		return;
	}
	Node* p = head->next;//第一个节点
	Node* q=NULL;
	head->next = NULL;
	while (p){
		q = p;
		p = p->next;
		//头插
		q->next = head->next;
		head->next = q;
	}
}

//单链表写入文件

void Write(Node* head){
	if (head == NULL)
		return;
	//fopen()第二个参数 ,上面有贴图
	FILE* fp = fopen("write.txt", "w+");//创建一个可读写的文件
	if (fp == NULL){	//文件创建失败
		perror("fopen");
		return;
	}
	//pCur遍历指针
	Node* pCur = head->next;
	struct tm* local;
	while (pCur){
		fprintf(fp, "%-8s", pCur->name);
		fprintf(fp, "%-2d", pCur->age);
		fprintf(fp, "%-4s", pCur->sex);
		fprintf(fp, "%-50s", pCur->addr);

		local = localtime(&pCur->hiredate);//转为本地时间格式
		fprintf(fp,"%d-%d-%d ", local->tm_year + 1900, local->tm_mon + 1, local->tm_mday);//年月日
		fprintf(fp,"%s %d:%d:%d\n", wday[local->tm_wday], local->tm_hour, local->tm_min, local->tm_sec);//时分秒
		//fprintf(fp, "%s", pCur->hiredate);
		pCur = pCur->next;
	}
	//关闭文件
	fclose(fp);
}

//从链表中找相同省份的人

void Find(Node* head,char* str){
	if (head == NULL || head->next == NULL)
		return;
	if (str == NULL)
		return;
	//a表示追加
	FILE* fp = fopen("write.txt", "a");//追加的方式发开文件
	if (fp == NULL){		//fopen()打开文件按失败时,返回值为NULL
		perror("fopen");
		return;
	}
	Node* pCur = head->next;
	struct tm* local;
	fprintf(fp,"\n\n省份为%s的人信息如下:\n", str);
	while (pCur){
		if (strstr(pCur->addr, str) != NULL){
			fprintf(fp, "%-8s", pCur->name);
			fprintf(fp, "%-2d", pCur->age);
			fprintf(fp, "%-4s", pCur->sex);
			fprintf(fp, "%-50s", pCur->addr);

			local = localtime(&pCur->hiredate);//转为本地时间格式
			fprintf(fp, "%d-%d-%d ", local->tm_year + 1900, local->tm_mon + 1, local->tm_mday);//年月日
			fprintf(fp, "%s %d:%d:%d\n", wday[local->tm_wday], local->tm_hour, local->tm_min, local->tm_sec);
			//fprintf(fp, "%s", pCur->hiredate);
		}
		pCur = pCur->next;		
	}
	fclose(fp);
}

//查看重复记录,如果存在,则删除(该函数为测试,读者自己debug)


void Compare(Node* head){
	if (head == NULL){
		return;
	}
	//最少2个节点才查重
	if (head->next == NULL || head->next->next == NULL){
		return;
	}
	Node* p1 = head->next;
	Node* p2 = p1->next;
	Node* pTmp = head;
	for (; p1 != NULL; p1 = p1->next){
		for (pTmp = p1,p2=p1; p2 != NULL; p2 = p2->next, pTmp = pTmp->next){
		AAA:	if (strcmp(p1->name, p2->name) != 0)
				continue;
			else if (strcmp(p1->sex, p1->sex) != 0)
				continue;
			else if (strcmp(p1->addr, p2->addr) != 0)
				continue;
			else{//找到重复的信息了----删除
				Node* tmp = pTmp->next;
				pTmp->next = tmp->next;
				free(tmp->name);
				free(tmp);
				p2 = pTmp->next;
				goto AAA;//此处不得不用goto
			}

		}
	}

}

//变单链表为单循环链表,头结点保留(为约瑟夫准备)

void Change(Node* head){
	if (head == NULL)
		return;
	Node* pCur = head;
	while (pCur->next)
		pCur = pCur->next;
	pCur->next = head->next;//尾节点连接首节点(第一个节点)
}

//随机出队,确定被开除的人员(链表实现约瑟夫)

void Exit(Node* head){
	if (head == NULL)
		return;
	int exit = 0;//开除2个人
	int count = 0;//数到20的人被开除
	int i = 0;
	Node* pCur = head->next;
	Node* pPre = head;
	Node* pTmp = NULL;
	printf("被开除的人如下:\n");
	while (pCur){
		if (exit == 2)
			break;
		++count;
		if (count == 20){
			++exit;
			pTmp = pCur;//要删除的节点
			pCur = pCur->next;//后移
			pPre->next = pCur;//pPre为前驱

			fprintf(stdout,"%-8s", pTmp->name);
			fprintf(stdout, "%-2d", pTmp->age);
			fprintf(stdout, "%-4s", pTmp->sex);
			fprintf(stdout, "%-50s\n", pTmp->addr);

			//if (pTmp->name)		//这里释放内存有问题 ,不清楚原因,注意,这个可以做考点
			//	free(pTmp->name);
			free(pTmp);
			count = 0;//重新计数
			continue;//删除节点后pPre不能后移,使用pPre的目的就是为了方便删除节点
		}
		pPre = pPre->next;
		pCur = pCur->next;
	}
}

//释放单链表内存

void Destory(Node* head){
	if (head == NULL)
		return;
	Node* pCur = head;
	Node* pTmp = NULL;
	while (pCur){
		pTmp = pCur;
		pCur = pCur->next;
		/*if (ptmp->name){
			free(ptmp->name);
			ptmp->name = null;
		}*/
		if (pTmp){
			free(pTmp);
			pTmp = NULL;
		}
	}
}

//下次再给出具体的测试案例,晚安–》

  • 10
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值