C语言大作业:通讯录

相对于网上的通讯录,创新点:
 

1.支持导入导出excel表,并支持导出后直接打开

2.(精准)查询联系人采用先快排,再二分查找

(模糊)查询联系人,采用kmp算法进行字符串匹配

3.支持登录验证以及登录后改账号密码(引入conio.h中getch函数,密码*显示,支持密码的撤销)

4.快排接口采用函数指针,支持结构体不同规则排序


菜单


                   *           1.录入联系人信息       *   
                   *          2.查找联系人信息        *
                   *          3.删除联系人信息        *
                   *          4.修改联系人信息        *
                   *          5.查看整个通讯录        *
                   *          6.清空整个通讯录        *
                   *          7.按姓名排序通讯录     *
                   *          8.按生日排序通讯录     *
                   *          9.按号码排序通讯录     *
                   *          10.修改登录账号密码   *
                   *          11.导出为csv格式文件  *
                   *          12.导入csv格式文件      *
                   *          0.退出                            *

演示图

 

 剩下的大家自己测试

代码如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <stdbool.h>
#include<conio.h>
//静态数组最大值
#define NMAX_PHONE 15
#define NMAX_NAME 15
#define NMAX_SEX 3
#define NMAX_ADDRESS 20
#define NMAX_BIRTHDAY 10
#define NMAX_USER 15
#define NMAX_FILE 20
//联系人人数最大值
#define NMAX_CONTACT 50
//结构体长度
#define LEN sizeof(contact)
//结构体打印数据
#define DATA p[i].phonenum,p[i].name,p[i].sex,p[i].address,p[i].birthday
//结构体打印数据占位符
#define FORMAT "\t\t  ||  %-15s%-16s%-14s%-15s%-8s ||\n"
typedef struct contact {
	char phonenum[NMAX_PHONE] ;     //号码
	char name[NMAX_NAME];    //姓名
	char sex[NMAX_SEX];    //性别
	char address[NMAX_ADDRESS];    //地址
	char birthday[NMAX_BIRTHDAY];//生日
} contact;
contact p[NMAX_CONTACT];
//录入联系人信息
void in();
//显示菜单
void menu();
//查询联系人
void search();
//删除联系人
void del();
//显示联系人
void show();
//清空整个通讯录
void clear();
//修改联系人信息
void modify();
//结构体交换(排序用)
void swap(contact *person1, contact *person2);
//自定义结构体快排函数
void sort(contact contacts[], int left, int right, bool (*compare)(contact,contact ));
//从数据文件读取通讯录
int read();
//结构体写入数据文件
void write(int m);
//姓名排序规则
bool compare_name(contact person1, contact person2);
//姓名排序
void sort_name();
//生日排序规则
bool compare_birthday(contact person1, contact person2);
//生日排序
void sort_birthday();
//电话号码排序规则
bool compare_phonenum(contact person1, contact person2);
//电话号码排序
void sort_phonenum();
//登录验证
void login(char admin[], char pswd[]);
//清屏
void clear_screen();
//结构体姓名二分查找
int binary_search(contact contacts[], int left, int right, char name[]);
//管理员账号修改
void user_modify();
//读取管理员账号密码
int read_user(char administor[], char password[]);
//重新写入管理员账号密码
int write_user(char administor[], char password[]);
//通讯录导出csv格式
void export_to_csv();
//csv格式导入通讯录
void import_to_csv();
//打开文件函数
void file_open(char name[]);
//kmp模式串next数组预处理
void kmp_prepare(char str[],int ne[]);
//kmp模式串与主串匹配
bool kmp_match(char str[],int ne[],char str2[]);
//全局变量 管理员账号
char admin[NMAX_USER], pass[NMAX_USER];
void main() {
	system("color 5e\n");//改变控制台背景颜色
	read_user(admin, pass); //读取管理员账号密码
	login(admin, pass); //登录 验证管理员账号密码
	menu();//验证成功显示菜单
	int n;//菜单序号
	scanf("%d", &n);
	while (n) { //菜单序号0 退出程序
		switch (n) {
			case 1:
				in();
				break;
			case 2:
				search();
				break;
			case 3:
				del();
				break;
			case 4:
				modify();
				break;
			case 5:
				show();
				break;
			case 6:
				clear();
				break;
			case 7:
				sort_name();
				break;
			case 8:
				sort_birthday();
				break;
			case 9:
				sort_phonenum();
				break;
			case 10:
				user_modify();
				break;
			case 11:
				export_to_csv();
				break;
			case 12:
				import_to_csv();
				break;
			default:
				fflush(stdin);//防止输入字符导致缓冲区未清空scanf%d卡死
				printf("请输入正确的序号,请按任意键继续。");
				break;
		}
		getch();//子菜单停留,按任意键继续
		menu();//执行完子菜单后重新显示主菜单
		scanf("%d", &n);//重新进入主菜单输入子菜单序号
	}
}
//显示菜单
void menu() {
	clear_screen();
	printf("\n\n\n");
	printf("\t\t\t\t\t**************************************\n");
	printf("\t\t\t\t\t************通讯录管理系统************\n");
	printf("\t\t\t\t\t======================================\n");
	printf("\t\t\t\t\t*          1.录入联系人信息          *\n");
	printf("\t\t\t\t\t*          2.查找联系人信息          *\n");
	printf("\t\t\t\t\t*          3.删除联系人信息          *\n");
	printf("\t\t\t\t\t*          4.修改联系人信息          *\n");
	printf("\t\t\t\t\t*          5.查看整个通讯录          *\n");
	printf("\t\t\t\t\t*          6.清空整个通讯录          *\n");
	printf("\t\t\t\t\t*          7.按姓名排序通讯录        *\n");
	printf("\t\t\t\t\t*          8.按生日排序通讯录        *\n");
	printf("\t\t\t\t\t*          9.按号码排序通讯录        *\n");
	printf("\t\t\t\t\t*          10.修改登录账号密码        *\n");
	printf("\t\t\t\t\t*          11.导出为csv格式文件      *\n");
	printf("\t\t\t\t\t*          12.导入csv格式文件        *\n");
	printf("\t\t\t\t\t*          0.退出                    *\n");
	printf("\t\t\t\t\t======================================\n");
	printf("\t\t\t\t\t**************************************\n");
	printf("\t\t\t\t\t**************************************\n");
	printf("\t\t\t\t\t          请选择(0-12):");
}
//录入联系人信息
void in() {
	int i, m = read();
	char ch[2];
	FILE* fp;
	if (m == 0) {
		printf("\n\t\t  暂无联系人! \n");
	} else {
		show();
	}
	if ((fp = fopen("PersonInfo.txt", "a+")) == NULL) {
		fclose(fp);
		printf("打开文件失败\n");
		return;
	}
	printf("\t\t  是否输入联系人信息(y/n):");
	scanf("%s", ch);
	while (strcmp(ch, "Y") == 0 || strcmp(ch, "y") == 0) {
		printf("\t\t  电话:");
		scanf("%s", p[m].phonenum);
		for (i = 0; i < m; i++) {
			if (strcmp(p[m].phonenum, p[i].phonenum) == 0) {
				printf("\t\t  此用户已经存在了,按任意键继续!");
				fclose(fp);
				return;
			}
		}

		printf("\t\t  姓名:");
		scanf("%s", p[m].name);
		for (i = 0; i < m; i++) {
			if (strcmp(p[m].name, p[i].name) == 0) {
				printf("\t\t  此用户已经存在了,按任意键继续!");
				fclose(fp);
				return;
			}
		}
		printf("\t\t  性别:");
		scanf("%s", p[m].sex);
		printf("\t\t  地址:");
		scanf("%s", p[m].address);
		printf("\t\t  生日:");
		scanf("%s", p[m].birthday);
		//fwrite返回n/size(n-写入字节数, size-每个数据块字节大小)
		if (fwrite(&p[m], LEN, 1, fp) != 1) {
			printf("\t\t  不能保存");
			getch();
		} else {
			printf("\t\t  %s 已被保存!\n", p[m].name);
			m++;
		}
		printf("\t\t  继续?(y/n):");
		scanf("%s", ch);
	}
	fclose(fp);
	printf("\t\t  结束录入!\n");
}
//删除联系人
void del() {
	char name[NMAX_NAME], i, j, m = read();
	int flag = 0;   //判断是否找到联系人
	char ch[2];		//choice
	if (m == 0) {
		printf("\n\t\t  暂无联系人!按任意键继续 \n");
		return;
	}
	show();
	printf("\t\t  输入要删除的联系人的姓名:");
	scanf("%s", name);
	for (i = 0; i < m; i++)
		if (strcmp(name, p[i].name) == 0) {

			printf("\t\t  找到联系人是否删除(y/n):");
			scanf("%s", ch);
			if (strcmp(ch, "Y") == 0 || strcmp(ch, "y") == 0) {
				for (j = i + 1; j < m; j++)
					p[j - 1] = p[j];
				m--;
				write(m);
				printf("\t\t  删除成功\n");
				show();
			} else {
				printf("\t\t  找到了联系人但未删除");
			}
			flag = 1;    //标志找到该联系人
			break;
		}
	if (flag == 0)
		printf("\t\t  没有找到该联系人!\n");
}
//查询联系人
void search() {
	char name[NMAX_NAME], ch[2];
	int ch2;
	int m = read();
	if (m == 0) {
		printf("\n\t\t  暂无联系人!按任意键继续 \n");
		return;
	}
	show();
	printf("\t\t  是否开始查询(y/n):");
	scanf("%s", ch);
	while (strcmp(ch, "Y") == 0 || strcmp(ch, "y") == 0) {
		printf("\n\t\t  请输入查询模式精准(1)模糊(2):");
		scanf("%d",&ch2);
		if(ch2==1){//精准查询(快排+二分)
			printf("\n\t\t  请输入联系人姓名:");
			scanf("%s", name);
			sort(p, 0, m - 1, compare_name);
			//二分返回可能的下标
			int i = binary_search(p, 0, m - 1, name);
			if (strcmp(name, p[i].name) == 0) {//判断是否可能
				printf("\t\t  ||  电话           姓名           性别           住址           生日     ||\n");
				printf(FORMAT, DATA);
			} else {
				printf("\t\t  没有找到记录!\n");
			}
		}else{		//模糊查询(kmp)
			printf("\n\t\t  请输入某部分姓名:");
			scanf("%s", name);
			int ne[NMAX_NAME],flag=0;//ne-模式串next数组构造 flag标志是否匹配到
			kmp_prepare(name,ne);
			for(int i=0;i<m;i++){
				if(kmp_match(name,ne,p[i].name)){
					flag++;
					if(flag==1){
						printf("\t\t  ||  电话           姓名           性别           住址           生日     ||\n");
					}
					printf(FORMAT, DATA);
				}
			}
			if(!flag){
				printf("\t\t  没有找到记录!\n");
			}
			
		}
		printf("\t\t  是否继续查询(y/n):");
		scanf("%s", ch);
	}
	printf("请按任意键继续");
}
//显示联系人
void show() {
	int i, m = read();
	printf("\t\t  ---------------------------------------------------------------------------\n");
	printf("\t\t  ||  电话           姓名           性别           住址           生日     ||\n");
	for (i = 0; i < m; i++) {
		printf(FORMAT, DATA);
	}
	printf("\t\t  ---------------------------------------------------------------------------\n");
}
//清除整个通讯录
void clear(){
	char ch[2];
	printf("\t\t  是否确认删除全部联系人(y/n):");
	scanf("%s",ch);
	if(strcmp(ch,"Y")==0|| strcmp(ch,"y")==0){
		write(0);
		printf("删除成功!");
	}
}
//从数据文件读取通讯录
int read() {
	int m = 0;
	FILE* fp;
	if ((fp = fopen("PersonInfo.txt", "a+")) == NULL) {
		printf("打开文件失败\n");
		fclose(fp);
		return 0;
	}
	while (!feof(fp)) {//feof文件未到末尾返回0,到末尾返回非0值
		if (fread(&p[m], LEN, 1, fp) == 1)
			m++;
	}
	fclose(fp);
	return m;
}
//结构体写入数据文件
void write(int m) {
	FILE *fp;
	if ((fp = fopen("PersonInfo.txt", "w")) == NULL) {
		fclose(fp);
		printf("打开文件失败\n");
		return;
	} else {
		if(!m){//如果传参m人数为0,则写空文件
			fprintf(fp,"");
			fclose(fp);
			return;
		}
		//fwrite返回n/size(n-写入字节数, size-每个数据块字节大小)
		for (int i = 0; i < m; i++) {
			if (fwrite(&p[i], LEN, 1, fp) != 1) {
				printf("\t\t  不能保存");
				fclose(fp);
				return;
			}
		}
	}
	fclose(fp);
}
//结构体交换(排序用)
void swap(contact *person1, contact *person2) {
	contact temp=*person1;
	*person1 = *person2;
	*person2 = temp;
}
//自定义结构体快排函数
//compare函数指针: 返回值 第一个参数是否大于第二个参数
void sort(contact contacts[], int left, int right, bool (*compare)(contact person1,contact person2) ) {
	if (left >= right)return;//越界递归停止
	int i = left - 1, j = right + 1;
	contact mid_contact = contacts[i + j >> 1];
	while (i < j) {
		do i++;
		while (compare(mid_contact, contacts[i]));
		do j--;
		while (compare(contacts[j], mid_contact));
		if (i < j)swap(&contacts[i], &contacts[j]);
	}
	//对新分界线再进行递归左排右排
	sort(contacts, left, j, compare), sort(contacts, j + 1, right, compare);
}
//姓名排序规则
bool compare_name(contact person1, contact person2) {
	if (strcmp(person1.name, person2.name) > 0)return true;
	else return false;
}
//姓名排序
void sort_name() {
	int m = read();
	sort(p, 0, m - 1, compare_name);
	write(m);
	show();
}
//生日排序规则
bool compare_birthday(contact person1, contact person2) {
	if (strcmp(person1.birthday, person2.birthday) > 0)return true;
	else return false;
}
//生日排序
void sort_birthday() {
	int m = read();
	sort(p, 0, m - 1, compare_birthday);
	write(m);
	show();
}
//电话号码排序规则
bool compare_phonenum(contact person1, contact person2) {
	if (strcmp(person1.phonenum, person2.phonenum) > 0)return true;
	else return false;
}
//电话号码排序
void sort_phonenum() {
	int m = read();
	sort(p, 0, m - 1, compare_phonenum);
	write(m);
	show();
}
//登录验证
void login(char admin[], char pswd[]) {
	int i;
	char c, adminstrator[NMAX_USER], password[NMAX_USER];
out://密码错误重新输入
	i = 0; //重置i,j值,若密码错误重新输入,i从0重新开始数组password从0继续存储
	printf("\n\n%70s\n\n\n\n", "登        录        界        面");
	printf("%40s", "账 号:");
	scanf(" %s", adminstrator);
	printf("\n\n%40s", "密 码:");
	while ((c = getch()) != '\r') {//没敲回车一直读
		if (c == '\b') {//如果是退格键执行退格
			if (i >= 1) {//必须输入密码≥1才能用退格键
				i--;
				printf("\b \b");
			}
			continue;//跳过下方密码存储
		}
		printf("*");
		password[i++] = c;	
	}
	password[i] = '\0';//便于下方strcmp比较
	if (strcmp(password, pswd) == 0 && strcmp(adminstrator, admin) == 0) {
		puts("\n\n验证成功,请等待......");
		Sleep(2000);
		clear_screen();
	}else{
		puts("\n\n账号或密码错误,按任意键重新验证!");
		getch();
		clear_screen();
		goto out;
	}
}
//清屏
void clear_screen() {
	system("cls");
}
//修改联系人信息
void modify() {
	int m = read(), flag = 0;//判断是否找到联系人
	char name[NMAX_NAME];
	if (m == 0) {
		printf("\n\t\t  暂无联系人!按任意键继续 \n");
		return;
	}
	show();
	printf("请输入要修改的联系人姓名:");
	scanf("%s", name);
	for (int i = 0; i < m; i++) {
		if (strcmp(p[i].name, name) == 0) {
			flag = 1;
			printf("请重新输入以下信息:\n");
			printf("\t\t  电话:");
			scanf("%s", p[i].phonenum);
			printf("\t\t  姓名:");
			scanf("%s", p[i].name);
			printf("\t\t  性别:");
			scanf("%s", p[i].sex);
			printf("\t\t  地址:");
			scanf("%s", p[i].address);
			printf("\t\t  生日:");
			scanf("%s", p[i].birthday);
			write(m);
			printf("修改成功!");
			break;
		}
	}
	if (flag == 0) {
		printf("联系人不存在!");
		return;
	}
}
//结构体姓名二分查找
int binary_search(contact contacts[], int left, int right, char name[]) {
	int i = left - 1, j = right + 1;
	while (i + 1 != j) {
		int mid = i + j >> 1;
		if (strcmp(contacts[mid].name, name) < 0) {
			i = mid;
		} else {
			j = mid;
		}
	}
	return i + 1;
}
//读取管理员账号密码
int read_user(char administor[], char password[]) {
	int m = 0;
	FILE* fp;
	if ((fp = fopen("User.txt", "a+")) == NULL) {
		printf("打开文件失败\n");
		fclose(fp);
		return 0;
	}
	if (fread(administor, NMAX_USER, 1, fp) == 1)m++;
	if (fread(password, NMAX_USER, 1, fp) == 1)m++;
	fclose(fp);
	return m;
}
//重新写入管理员账号密码
int write_user(char administor[], char password[]) {
	int m = 0;
	FILE *fp;
	if ((fp = fopen("User.txt", "w")) == NULL) {
		fclose(fp);
		printf("打开文件失败\n");
		return 0;
	} 
	if (fwrite(administor, NMAX_USER, 1, fp) == 1)m++;
	if (fwrite(password, NMAX_USER, 1, fp) == 1)m++;
	fclose(fp);
	return m;
}
//管理员账号修改
void user_modify() {
	clear_screen();
	login(admin, pass);
	int i;
	char c, adminstrator[NMAX_USER], password[NMAX_USER];
	i = 0; //重置i,j值,若密码错误重新输入i从0重新开始数组password从0继续存储
	printf("\n\n%70s\n\n\n\n", "修        改        界        面");
	printf("%40s", "账 号:");
	scanf(" %s", adminstrator);
	printf("\n\n%40s", "密 码:");
	while ((c = getch()) != '\r') {
		if (c == '\b') {
			if (i >= 1) {
				i--;
				printf("\b \b");
			}
			continue;
		}
		printf("*");
		password[i++] = c;
	}
	password[i] = '\0';
	write_user(adminstrator, password);
	read_user(admin, pass);
	printf("\n\n修改成功,请重新登录!");
	getch();
	clear_screen();
	login(admin, pass);
}
//通讯录导出csv格式
void export_to_csv() {
	int m = read();
	char ch[2];
	FILE *fp = fopen("PersonInfo.csv", "w");
	if (fp == NULL) {
		printf("打开文件失败\n");
		return;
	}
	fprintf(fp, "电话号码,姓名,性别,地址,生日\n");
	for (int i = 0; i < m; i++) {
		fprintf(fp, "%s,%s,%s,%s,%s\n", p[i].phonenum, p[i].name, p[i].sex, p[i].address, p[i].birthday);
	}
	fclose(fp);
	printf("导出成功,文件名为PersonInfo.csv(位于同目录下),是否打开(y/n):");
	scanf("%s", ch);
	if (strcmp(ch, "Y") == 0 || strcmp(ch, "y") == 0) {
		file_open("PersonInfo.csv");
		printf("打开成功!\n");
	}
	printf("请按任意键继续");
}
//csv格式导入通讯录
void import_to_csv() {
	char filename[NMAX_FILE];
	int m = 0, num_success;
	printf("请输入需要导入是csv文件名(需带上后缀名):");
	scanf("%s", filename);
	FILE *fp = fopen(filename, "r");
	if (fp == NULL) {
		printf("打开文件失败\n");
		fclose(fp);
		return ;
	}
	if (!feof(fp)) { //过滤标题
		fscanf(fp, "%*s%*c");
	}
	while (!feof(fp) && (num_success = fscanf(fp, "%[^,],%[^,],%[^,],%[^,],%[^\n]\n", &p[m].phonenum, &p[m].name, &p[m].sex, &p[m].address, &p[m].birthday)) == 5) {
		m++;
	}
	fclose(fp);
	write(m);
	printf("导入成功!");
}
//打开文件函数
void file_open(char name[]) {
	char res[NMAX_FILE+7] = "start ";
	strncat(res, name,NMAX_NAME);
	system(res);
}
//kmp模式串next数组预处理
void kmp_prepare(char str[],int ne[]){
	ne[0]=-1;
	for(int i=1,j=-1;i<strlen(str);i++){
		while(j!=-1&&str[i]!=str[j+1])j=ne[j];
		if(str[i]==str[j+1])j++;
		ne[i]=j;
	}
}
//kmp模式串str与主串str2匹配
bool kmp_match(char str[],int ne[],char str2[]){
	for(int i=0,j=-1;i<strlen(str2);i++){
		while(j!=-1&&str2[i]!=str[j+1])j=ne[j];
		if(str2[i]==str[j+1])j++;
		if(j==strlen(str)-1){
			return true;
		}
	}
	return false;
}

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值