相对于网上的通讯录,创新点:
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;
}