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;
}
}
}
//下次再给出具体的测试案例,晚安–》