#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100
#define TRUE 1
#define FALSE 0
#define Status int
#define fname "txl.txt"
typedef struct //成员信息
{
char ID[12];
char name[12];
char mphone[12];
char addr[40];
char tel[16];
}
DataType;
typedef struct node //数据结点
{
DataType data;
struct node *next;
}
Node;
typedef Node *Llist; //结点指针
char *menu_1[] = {
"**********菜单*********\n",
"1.添加好友信息\n",
"2.列表好友信息\n",
"3.搜索好友信息\n",
"4.删除好友信息\n"
"0.退出程序\n",
"***********************\n" };
/*********************************************************************************************
函数名称:fgets_wrapper
参数:buffer输入缓冲区指针;buflen最大输入字符数,实际最大输入buflen-1; fp输入流
功能:重新封装fgets函数;
解决fgets输入时最大长度超过buflen-1个字符后输入缓冲区遗留问题及输入包含'\n'问题
区别: scanf("%s",buffer) 输入字符串不能有空格;
gets(buffer)输入字符串超过buffer长度导致溢出错误;
fgets(buffer,buflen,fp)最大输入buflen-1个字符,'\n'会被输入;输入被截断后输入缓冲区有遗留
***********************************************************************************************/
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp)
{
if (fgets(buffer, buflen, fp) != 0)
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n')
{
buffer[len - 1] = '\0';
}
//清空剩余的数据
else
{
int ch;
while ((ch = getc(fp)) != EOF && ch != '\n');
}
return buffer;
}
return 0;
}
char menu(char *str[], int len) //菜单函数,让用户选择功能
{
char sel;
int i;
for (i = 0; i < len; i++)
{
printf("%s", str[i]);
}
printf("请输入您的选择:");
scanf(" %c", &sel);
getchar();
return sel;
}
Status InitList(Llist *L) //初始化链表
{
*L = (Llist)malloc(sizeof(Node)); //分配头结点的内存空间
if (NULL == *L) //分配失败
{
return FALSE;
}
else //分配成功
{
(*L)->next = NULL;
return TRUE;
}
}
Status ListLength(Llist L) //测量链表长度(返回成员个数)
{
int i = 0;
Llist p;
p = L->next;
while (NULL != p)
{
i++;
p = p->next;
}
return i;
}
Status ListEmpty(Llist L) //判断链表是否为空
{
if (0 == ListLength(L))
{
return TRUE;
}
else
{
return FALSE;
}
}
Status ListInsert(Llist *L, int i, DataType e) //将e插入链表的第i个位置
{
int j = 1;
Llist p, q;
if (i < 1 || i > ListLength(*L) + 1)
{
return FALSE;
}
p = (*L);
while (NULL != p && j < i) //p指向要插入位置的前一个
{
p = p->next;
j++;
}
q = (Llist)malloc(sizeof(Node));
//分配一个节点空间,将数据放入节点,再将节点插入链表
if (NULL == q)
{
printf("内存分配失败,按回车键退出...\n");
getchar();
exit(0);
}
q->data = e;
q->next = p->next;
p->next = q;
return TRUE;
}
Status GetElem(Llist L, int i, DataType *e) //取链表中第i个位置的数据
{
int j = 1;
Llist p;
if (i < 1 || i > ListLength(L))
{
return FALSE;
}
p = L;
while (NULL != p && j < i) //p指向要取数据位置的前一个
{
p = p->next;
j++;
}
p = p->next;
*e = p->data;
return TRUE;
}
Status ListDelete(Llist *L, int i, DataType *e) //删除链表中第i个位置的数据
{
int j = 1;
Llist p, q;
if (i < 1 || i > ListLength(*L))
{
return FALSE;
}
p = (*L);
while (NULL != p && j < i) //p指向要删除数据位置的前一个
{
p = p->next;
j++;
}
q = p->next; //先将要删位置的next赋给前一个位置的next
p->next = q->next;
//将删除位置数据用e返回,释放删除的节点
*e = q->data;
free(q);
return TRUE;
}
Status ClearList(Llist *L) //清空链表
{
int i = 0;
Llist p, q;
if (NULL == (*L)->next)
{
return TRUE;
}
p = q = (*L)->next;
do
{
p = p->next;
free(q);
i++;
q = p;
} while (NULL != p);
//删除一个free一个,将头节点的指针置空
(*L)->next = NULL;
printf("cleared item = %d\n", i);
return TRUE;
}
Status Input(Llist L, DataType *e) //输入函数
{
Llist p;
int i = 0;
p = L->next;
printf("请输入ID:");
fgets_wrapper(e->ID, 12, stdin);
for (p; NULL != p; p = p->next) //判断输入ID是否存在,存在则重新输入
{
if (!strcmp(p->data.ID, e->ID))
{
printf("此ID已存在,请重新输入ID\n");
Input(L, e);
i++;
}
}
if (1 == i) //通过i的值判断程序是否往下执行
{
return TRUE;
}
else
{
printf("请输入姓名:");
fgets_wrapper(e->name, 12, stdin);
printf("请输入手机号:");
fgets_wrapper(e->mphone, 12, stdin);
printf("请输入住址:");
fgets_wrapper(e->addr, 40, stdin);
printf("请输入电话:");
fgets_wrapper(e->tel, 16, stdin);
}
printf("按回车键继续...\n");
getchar();
return TRUE;
}
Status Output(DataType e) //输出函数
{
printf("好友ID:%s\n", e.ID);
printf("姓名:%s\n", e.name);
printf("手机号码:%s\n", e.mphone);
printf("家庭住址:%s\n", e.addr);
printf("公司电话:%s\n", e.tel);
return TRUE;
}
void ListSort(Llist L) //按姓名冒泡排序
{
DataType temp;
Llist p, q;
for (p = L; NULL != p->next; p = p->next)
{
for (q = p->next; NULL != q; q = q->next)
{
if (strcmp(p->data.name, q->data.name) > 0)
{
temp = p->data;
p->data = q->data;
q->data = temp;
}
}
}
}
Status List(Llist L) //列表信息 先调用排序,再依次输出
{
ListSort(L);
int i;
Llist p;
if (NULL == L->next)
{
printf("当前还没有任何记录\n");
printf("按回车键继续...\n");
getchar();
}
else
{
p = L->next;
i = 1;
while (NULL != p)
{
printf("第%d条记录\n", i);
Output(p->data);
printf("\n");
p = p->next;
i++;
}
printf("按回车键继续...\n");
getchar();
}
return TRUE;
}
Status *LocateList(Llist L, char *str) //在链表中找名字与实参相同的数据,返回数据的位置
{
//返回的数组中名字不相同的位置的值为-1,
int i, j;
Llist p;
p = L->next;
j = 0;
int *a;
a = (int *)malloc(sizeof(Node)); //为每一个Node大小的成员分配一个指针
*(a + 0) = -1; //*(a+0)值为-1 不变,不使用
if (NULL == L->next)
{
return a;
}
else
{
i = 1;
while (NULL != p)
{
if (!strcmp(str, p->data.name))
//相同则将位置i赋到数组的对应位置
{
j++;
*(a + j) = i;
i++;
p = p->next;
}
else
//不同则将该位置对应的数组值置-1
{
j++;
*(a + j) = -1;
i++;
p = p->next;
}
}
return a;
}
}
Status LocateList_num(Llist L, char *str) //在链表中找ID与实参相同的数据,返回数据的位置
{
int i;
Llist p;
p = L->next;
if (NULL == L->next)
{
return -1;
}
i = 1;
while (NULL != p)
{
if (!strcmp(str, p->data.ID)) //找到返回位置i,没找到返回-1
{
return i;
}
else
{
p = p->next;
i++;
}
}
return -1;
}
Status Query(Llist L) //按姓名搜索信息
{
char name[12];
int i;
DataType e;
Status flag = 0;
//标志flag判断是否搜索到信息
int *q;
printf("请输入姓名:");
fgets_wrapper(name, 12, stdin);
q = LocateList(L, name);
for (i = 0; i <= ListLength(L); i++) //只有一个输出一个 有多个同名则同名信息全部输出
{
if (-1 != *(q + i))
{
printf("查询到记录,显示如下:\n");
GetElem(L, *(q + i), &e);
Output(e);
flag = 1;
//查询到信息,将flag置1
printf("\n");
}
}
if (0 == flag)
{
printf("查无此人,按回车键继续...");
getchar();
}
else
{
printf("按回车键继续...\n");
getchar();
}
return TRUE;
}
Status Delete(Llist *L) //按姓名删除信息,同名则按ID删除
{
char name[12];
char ID[12];
int *str;
DataType e;
int i = 0, j = 0;
printf("请输入姓名:");
fgets_wrapper(name, 12, stdin);
str = LocateList(*L, name); //接收按姓名定位的返回值(数组)
for (i = 1; i <= ListLength(*L); i++)
{
if (-1 != *(str + i))
{
j++;
//判断返回数组中值为-1的个数
}
}
if (0 == j) //0个则没找到该信息
{
printf("无此人,按回车键继续...\n");
getchar();
}
else if (j == 1)
{
for (i = 1; i <= ListLength(*L); i++)
{
if (-1 != *(str + i)) //找到一个,确定其位置,先输出一下再删除
{
printf("有此人,信息如下:\n");
GetElem(*L, *(str + i), &e);
Output(e);
ListDelete(L, *(str + i), &e);
printf("已删除\n");
printf("按回车键继续\n");
getchar();
}
}
}
else
{
for (i = 1; i <= ListLength(*L); i++) //有同名信息,逐个输出
{
if (-1 != *(str + i))
{
printf("有此人,信息如下:\n");
GetElem(*L, *(str + i), &e);
Output(e);
}
}
printf("有多个同名好友,按ID号删除\n");
printf("请输入ID:");
fgets_wrapper(ID, 12, stdin);
j = LocateList_num(*L, ID); //获取要删除ID的位置,先输出再删除即可
if (-1 != j)
{
GetElem(*L, j, &e);
Output(e);
ListDelete(L, j, &e);
printf("已删除\n");
printf("按回车键继续...\n");
getchar();
}
else
{
printf("查无此人,按回车键继续...\n");
getchar();
}
}
return TRUE;
}
Status Readfile(Llist *L) //将文件里的数据读到链表中
{
FILE *fp;
DataType e;
int i, size;
if ((fp = fopen(fname, "a+")) == NULL) //打开文件 标准I/O返回值为指针
{
perror("打开文件失败");
return FALSE;
}
fseek(fp, 0, SEEK_END);
//将文件指针移到文件尾
size = ftell(fp) / sizeof(DataType); // 计算文件中数据的个数 ftell计算文件大小,除以数据类型的大小
rewind(fp); //将文件指针移到文件头 与fseek(fp,0,SEEK_SET)功能一致
for (i = 0; i < size; i++)
{
fread(&e, sizeof(DataType), 1, fp); //将数据一个一个读到e中
ListInsert(L, ListLength(*L) + 1, e); //将e尾插进链表
}
fclose(fp);
//标准I/O需要关闭文件
return TRUE;
}
Status Writefile(Llist *L) //将数据从链表中写入文件
{
FILE *fp;
DataType e;
Llist p = (*L)->next;
if ((fp = fopen(fname, "w+")) == NULL) //打开文件
{
perror("打开文件失败");
return FALSE;
}
while (p != NULL) //将数据逐个写入文件
{
e = p->data;
fwrite(&e, sizeof(DataType), 1, fp);
p = p->next;
}
fclose(fp); //关闭文件
return TRUE;
}
int main(int argc, char *argv[])
{
Llist txl;
Status flag;
DataType e;
char sel;
flag = InitList(&txl);
//判断链表初始化是否成功
if (!flag)
{
printf("内存分配失败,按回车键退出...\n");
getchar();
exit(0);
}
if (FALSE == Readfile(&txl)) //判断读文件是否成功
{
printf("文件打开失败\n");
exit(1);
}
while (1)
{
sel = menu(menu_1, 7); //调用菜单函数
switch (sel)
{ // 数据尾插进入链表
case '1': memset(&e, 0, sizeof(DataType)); Input(txl, &e); ListInsert(&txl, ListLength(txl) + 1, e); printf("添加成功\n");
break;
case '2': List(txl);
break;
case '3': Query(txl);
break;
case '4': Delete(&txl);
break;
case '0': printf("谢谢使用本软件,按回车键退出...\n");
Writefile(&txl);
getchar();
exit(0);
break;
default: printf("错误输入\n");
break;
}
}
return 0;
}