一、数据结构升级:更完善的联系人模型
为满足新需求,我们需要扩展联系人结构体,增加电话和住址字段:
#define MAX_NAME 20
#define MAX_PHONE 12
#define MAX_ADDR 50
#define MAX_CONTACTS 1000
typedef struct Person {
char name[MAX_NAME];
char gender[5]; // "男"/"女"/"保密"
int age;
char phone[MAX_PHONE];
char address[MAX_ADDR];
} Person;
typedef struct AddressBook {
Person contacts[MAX_CONTACTS];
int count; // 当前联系人数量
} AddressBook;优化点:
- 使用
typedef简化类型名 - 字符串长度使用宏定义便于维护
- 性别改用字符串存储更灵活
二、功能实现详解
- 初始化与清空功能
// 初始化通讯录
void initAddressBook(AddressBook *ab) {
ab->count = 0;
// 加载文件数据(实现略,参考上节课)
}
// 清空通讯录
void clearAddressBook(AddressBook *ab) {
if (confirm("确定要清空所有联系人吗?")) {
ab->count = 0;
saveToFile(ab); // 保存空文件
printf("通讯录已清空!\n");
}
}关键点:
- 清空操作需二次确认
- 同步更新文件确保数据一致性
- 添加联系人(带输入验证)
void addContact(AddressBook *ab) {
if (ab->count >= MAX_CONTACTS) {
printf("通讯录已满!\n");
return;
}
Person p;
printf("请输入姓名:");
scanf("%s", p.name);
// 性别验证
while (1) {
printf("请输入性别(男/女/保密):");
scanf("%s", p.gender);
if (strcmp(p.gender, "男")==0 || strcmp(p.gender, "女")==0 ||
strcmp(p.gender, "保密")==0) break;
printf("输入无效,请重新输入!\n");
}
// 年龄验证
while (1) {
printf("请输入年龄:");
scanf("%d", &p.age);
if (p.age > 0 && p.age < 150) break;
printf("年龄无效,请重新输入!\n");
}
// 电话验证(11位数字)
while (1) {
printf("请输入电话:");
scanf("%s", p.phone);
if (strlen(p.phone) == 11 && isdigitString(p.phone)) break;
printf("电话号码格式错误,请重新输入!\n");
}
printf("请输入住址:");
scanf("%s", p.address);
ab->contacts[ab->count++] = p;
saveToFile(ab);
printf("联系人添加成功!\n");
}输入验证技巧:
- 使用循环确保输入符合格式
- 自定义
isdigitString()函数检查全数字字符串
- 删除与查找功能
// 查找联系人(返回索引,未找到返回-1)
int findContact(const AddressBook *ab, const char *name) {
for (int i = 0; i < ab->count; i++) {
if (strcmp(ab->contacts[i].name, name) == 0) {
return i;
}
}
return -1;
}
// 删除联系人
void deleteContact(AddressBook *ab) {
char name[MAX_NAME];
printf("请输入要删除的联系人姓名:");
scanf("%s", name);
int index = findContact(ab, name);
if (index == -1) {
printf("未找到该联系人!\n");
return;
}
// 前移覆盖删除
for (int i = index; i < ab->count - 1; i++) {
ab->contacts[i] = ab->contacts[i + 1];
}
ab->count--;
saveToFile(ab);
printf("联系人已删除!\n");
}删除优化:
- 使用数组前移实现逻辑删除
- 同步更新联系人数量
- 修改与显示功能
// 修改联系人信息
void modifyContact(AddressBook *ab) {
char name[MAX_NAME];
printf("请输入要修改的联系人姓名:");
scanf("%s", name);
int index = findContact(ab, name);
if (index == -1) {
printf("未找到该联系人!\n");
return;
}
// 选择性修改字段(示例略)
// 提示用户选择要修改的字段,重新输入对应内容
saveToFile(ab);
printf("联系人信息已更新!\n");
}
// 显示所有联系人
void displayAll(const AddressBook *ab) {
if (ab->count == 0) {
printf("通讯录为空!\n");
return;
}
// 格式化输出表头
printf("%-10s%-6s%-6s%-13s%-20s\n",
"姓名", "性别", "年龄", "电话", "住址");
printf("--------------------------------------------\n");
// 循环输出所有联系人
for (int i = 0; i < ab->count; i++) {
printf("%-10s%-6s%-6d%-13s%-20s\n",
ab->contacts[i].name,
ab->contacts[i].gender,
ab->contacts[i].age,
ab->contacts[i].phone,
ab->contacts[i].address);
}
}显示优化:
- 使用固定宽度格式化输出
- 添加表头提高可读性
- 排序功能实现
// 比较函数(用于qsort)
int compareByName(const void *a, const void *b) {
return strcmp(((Person*)a)->name, ((Person*)b)->name);
}
// 按姓名排序
void sortByName(AddressBook *ab) {
if (ab->count <= 1) return;
qsort(ab->contacts, ab->count, sizeof(Person), compareByName);
saveToFile(ab);
printf("通讯录已按姓名排序!\n");
}排序关键点:
- 使用标准库
qsort函数 - 自定义比较函数实现字典序排序
三、文件操作升级
为保证数据安全,我们采用二进制文件存储,并添加版本控制:
// 文件头结构(添加版本信息)
typedef struct {
int version; // 文件版本号
int contactCount; // 联系人数量
} FileHeader;
// 保存到文件
void saveToFile(const AddressBook *ab) {
FILE *fp = fopen("contacts.dat", "wb");
if (fp == NULL) {
perror("无法保存文件");
return;
}
// 写入文件头
FileHeader header = {1, ab->count};
fwrite(&header, sizeof(FileHeader), 1, fp);
// 写入所有联系人
fwrite(ab->contacts, sizeof(Person), ab->count, fp);
fclose(fp);
}
// 从文件加载
void loadFromFile(AddressBook *ab) {
FILE *fp = fopen("contacts.dat", "rb");
if (fp == NULL) return; // 文件不存在,创建新通讯录
// 读取文件头
FileHeader header;
fread(&header, sizeof(FileHeader), 1, fp);
// 检查版本兼容性
if (header.version != 1) {
printf("文件版本不兼容!\n");
fclose(fp);
return;
}
// 读取联系人
int count = header.contactCount;
if (count > MAX_CONTACTS) count = MAX_CONTACTS;
fread(ab->contacts, sizeof(Person), count, fp);
ab->count = count;
fclose(fp);
}文件优化:
- 添加版本号支持未来扩展
- 限制最大读取数量防止溢出
- 错误处理更完善
四、用户界面优化
主菜单增加颜色和分隔线,提升用户体验:
void showMenu() {
system("cls"); // 清屏(Windows)
printf("\033[32m"); // 设置绿色文字
printf("========================================\n");
printf(" 高级通讯录管理系统 v1.0\n");
printf("========================================\n");
printf("\033[34m"); // 设置蓝色文字
printf(" 1. 添加联系人 2. 删除联系人\n");
printf(" 3. 查找联系人 4. 修改联系人\n");
printf(" 5. 显示所有 6. 清空通讯录\n");
printf(" 7. 按姓名排序 8. 退出系统\n");
printf("\033[0m"); // 恢复默认颜色
printf("----------------------------------------\n");
printf("请选择操作(1-8):");
}界面美化:
- 使用ANSI转义序列实现彩色输出
- 添加清屏功能保持界面整洁
- 菜单布局更合理
五、项目扩展建议
- 动态内存管理:改用链表结构,彻底解决容量限制
- 数据加密:文件存储时对敏感信息加密(如电话号码)
- 高级查询:支持按地址、年龄范围等多条件查询
- 备份功能:自动创建历史版本备份,防止数据丢失
- 导入导出:支持CSV、JSON等格式,方便数据交换
总结:
通过本节课的学习,我们掌握了一个完整的C语言项目开发流程,包括:
- 复杂数据结构设计
- 模块化函数开发
- 输入验证与错误处理
- 文件持久化存储
- 排序算法应用
- 用户界面优化
通讯录项目不仅是C语言基础的综合实践,更是培养编程思维的重要环节。建议大家动手实现这个系统,并尝试添加自己的特色功能,进一步提升编程能力!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。