项目简介
这是一个功能完善的通讯录管理系统,使用C语言开发,采用控制台交互界面。系统支持联系人信息的增删改查、多种查询方式、数据导入导出、自动备份等完整功能。
主要特性
- 完整的联系人管理:添加、删除、修改、查询联系人信息
- 丰富的联系人字段:姓名、固定电话、手机、邮箱、地址、分组、备注
- 灵活的查询功能:按姓名、手机号、分组等多种方式查询
- 分组管理:支持联系人分组,便于分类管理
- 数据持久化:CSV格式存储,易于查看和编辑
- 数据备份功能:自动备份,支持数据恢复
- 友好的用户界面:彩色输出、清晰的菜单结构
- 数据验证:输入验证,确保数据有效性
运行效果
###1.主页面

2.加载页面

3.系统设置页面

4.数据查询页面

等等页面
快速开始
编译方法
Windows (MinGW)
gcc contact_manager.c -o contact_manager.exe
Linux/Mac
gcc contact_manager.c -o contact_manager
运行程序
Windows
contact_manager.exe
Linux/Mac
./contact_manager
功能详细说明
1. 联系人管理
1.1 添加联系人
- 功能:录入新联系人的完整信息
- 输入项:
- 姓名*(必填,不能重复)
- 固定电话(可选)
- 手机号码(可选)
- 电子邮箱(可选,带格式验证)
- 地址(可选)
- 分组(可选,如:家人、朋友、同事等)
- 备注(可选)
- 数据验证:
- 姓名不能为空
- 姓名不能重复
- 邮箱格式验证(简单验证,检查是否包含@符号)
1.2 删除联系人
- 功能:删除指定联系人的信息
- 操作流程:
- 输入要删除的联系人姓名
- 系统显示联系人信息供确认
- 输入 Y 确认删除,N 取消
- 说明:采用软删除机制,数据可恢复
1.3 修改联系人
- 功能:修改已存在联系人的信息
- 可修改项:
- 姓名(会检查是否与其他联系人冲突)
- 固定电话
- 手机号码
- 电子邮箱
- 地址
- 分组
- 备注
- 说明:可以逐项修改,修改完成后返回
1.4 显示所有联系人
- 功能:以表格形式显示所有联系人的基本信息
- 显示内容:
- 姓名、固定电话、手机、邮箱、分组
- 说明:已删除的联系人不会显示
2. 数据查询
2.1 按姓名查询
- 功能:支持姓名模糊查询
- 操作:输入姓名关键字,系统会显示所有包含该关键字的联系人
- 显示内容:联系人的完整详细信息
- 适用场景:只记得姓名的一部分时使用
2.2 按手机号查询
- 功能:精确查询指定手机号对应的联系人
- 操作:输入完整的手机号码
- 显示内容:联系人的完整详细信息
- 适用场景:通过手机号查找联系人
2.3 按分组查询
- 功能:查询指定分组的所有联系人
- 操作:输入分组名称(如:家人、朋友等)
- 显示内容:该分组下所有联系人的完整信息
- 适用场景:查看某个分组的所有联系人
2.4 显示所有分组
- 功能:列出所有已存在的分组及其联系人数量
- 显示内容:
- 分组名称
- 该分组下的联系人数量
- 适用场景:了解通讯录的分组情况
3. 系统设置
3.1 保存数据
- 功能:将当前内存中的数据保存到CSV文件
- 默认文件:
contacts.csv - 说明:建议在退出程序前保存数据,避免数据丢失
3.2 备份数据
- 功能:创建数据备份文件
- 备份位置:
backups/目录 - 文件命名:
backup_YYYYMMDD_HHMMSS.csv - 说明:
- 备份文件包含时间戳,便于版本管理
- 自动创建备份目录(如果不存在)
- 建议定期备份,防止数据丢失
3.3 加载数据文件
- 功能:从CSV文件加载联系人数据
- 操作:可以指定文件名,默认加载
contacts.csv - 说明:
- 加载的数据会与现有数据合并
- 相同学名的数据会覆盖现有记录
- 支持从其他文件导入数据
3.4 导出CSV文件
- 功能:将数据导出到指定的CSV文件
- 用途:数据迁移、数据分享、Excel分析
- 格式:标准CSV格式,可用Excel打开
- 说明:可以导出到任意文件名
? 数据文件格式
CSV文件结构
系统使用CSV(逗号分隔值)格式存储数据,文件结构如下:
姓名,固定电话,手机,邮箱,地址,分组,备注
张三,010-12345678,13800138000,zhangsan@example.com,北京市朝阳区,朋友,大学同学
李四,021-87654321,13900139000,lisi@example.com,上海市浦东新区,同事,项目经理
字段说明
| 字段 | 类型 | 说明 | 必填 | 最大长度 |
|---|---|---|---|---|
| 姓名 | 字符串 | 联系人姓名,唯一标识 | ? | 50字符 |
| 固定电话 | 字符串 | 固定电话号码 | ? | 20字符 |
| 手机 | 字符串 | 手机号码 | ? | 20字符 |
| 邮箱 | 字符串 | 电子邮箱地址 | ? | 50字符 |
| 地址 | 字符串 | 联系地址 | ? | 100字符 |
| 分组 | 字符串 | 联系人分组 | ? | 30字符 |
| 备注 | 字符串 | 备注信息 | ? | 200字符 |
数据导入注意事项
- 文件编码:建议使用UTF-8编码,避免中文乱码
- 表头:第一行必须是表头,系统会自动识别
- 字段顺序:必须按照上述顺序排列
- 数据验证:
- 姓名不能为空
- 相同学名的数据会覆盖已有记录
- 邮箱格式会进行简单验证
- 特殊字符:字段中包含逗号或引号时,系统会自动处理
? 使用示例
示例1:添加联系人
1. 选择"联系人管理" -> "添加联系人"
2. 输入姓名:张三
3. 输入固定电话:010-12345678(可选)
4. 输入手机号码:13800138000(可选)
5. 输入电子邮箱:zhangsan@example.com(可选)
6. 输入地址:北京市朝阳区(可选)
7. 输入分组:朋友(可选)
8. 输入备注:大学同学(可选)
9. 系统保存联系人信息
示例2:查询联系人
1. 选择"数据查询" -> "按姓名查询"
2. 输入姓名关键字:张
3. 系统显示所有姓名包含"张"的联系人
或者:
1. 选择"数据查询" -> "按分组查询"
2. 输入分组:朋友
3. 系统显示所有"朋友"分组的联系人
示例3:修改联系人
1. 选择"联系人管理" -> "修改联系人"
2. 输入要修改的联系人姓名:张三
3. 选择要修改的字段(如:选择3修改手机号码)
4. 输入新的手机号码
5. 选择0完成修改
示例4:数据备份和恢复
1. 选择"系统设置" -> "备份数据"
2. 系统自动创建备份文件:backups/backup_20241201_143022.csv
恢复数据:
1. 选择"系统设置" -> "加载数据文件"
2. 输入备份文件名:backups/backup_20241201_143022.csv
3. 系统加载备份数据
技术实现
数据结构
typedef struct {
char name[NAME_LEN]; // 姓名(必填)
char phone[PHONE_LEN]; // 固定电话
char mobile[MOBILE_LEN]; // 手机号码
char email[EMAIL_LEN]; // 电子邮箱
char address[ADDRESS_LEN]; // 地址
char group[GROUP_LEN]; // 分组
char note[NOTE_LEN]; // 备注信息
int isDeleted; // 删除标记
} Contact;
核心算法
-
数据查找:
- 线性查找:O(n)
- 按姓名查找:精确匹配和模糊匹配
- 按手机号查找:精确匹配
- 适用于中小规模数据(<10000条)
-
数据存储:
- 使用静态数组存储,最大容量10000条
- CSV格式持久化,便于查看和编辑
- 支持软删除(isDeleted标记)
-
CSV解析:
- 支持引号包裹的字段
- 处理字段中的逗号和特殊字符
- 自动去除字段两端的空格和引号
平台兼容性
- Windows:完全支持,包含彩色输出和清屏功能
- Linux/Mac:基本功能支持,部分UI功能可能受限
配置说明
可配置参数
在源代码中可以修改以下常量:
#define NAME_LEN 50 // 姓名最大长度
#define PHONE_LEN 20 // 电话最大长度
#define MOBILE_LEN 20 // 手机最大长度
#define EMAIL_LEN 50 // 邮箱最大长度
#define ADDRESS_LEN 100 // 地址最大长度
#define GROUP_LEN 30 // 分组最大长度
#define NOTE_LEN 200 // 备注最大长度
#define MAX_CONTACTS 10000 // 最大联系人数量
#define DATA_FILE "contacts.csv" // 默认数据文件
自定义字段
如需添加新字段:
- 在
Contact结构体中添加新字段 - 在
add_contact()函数中添加输入逻辑 - 在
modify_contact()函数中添加修改逻辑 - 在
save_data()和load_data()函数中处理新字段 - 重新编译程序
? 注意事项
数据安全
- 定期备份:建议定期使用备份功能保存数据
- 数据验证:系统会对输入数据进行基本验证,但仍需谨慎输入
- 文件权限:确保程序对数据文件有读写权限
- 文件编码:使用UTF-8编码,避免中文乱码
使用建议
- 首次使用:建议先添加少量测试数据,熟悉操作流程
- 分组管理:合理使用分组功能,便于分类管理联系人
- 定期保存:修改数据后及时保存,避免数据丢失
- 备份策略:建议在重要操作前先备份数据
已知限制
- 数据规模:最大支持10000条联系人记录
- 并发访问:不支持多用户同时访问同一数据文件
- CSV解析:使用简单的CSV解析,不支持复杂的转义字符
- 邮箱验证:邮箱格式验证较简单,仅检查是否包含@符号
- 电话号码验证:电话号码格式验证较简单,仅检查长度
常见问题
Q1: 程序无法编译?
A: 检查以下几点:
- 确保已安装C编译器(GCC/MinGW)
- 检查源代码文件是否完整
- 查看编译错误信息,可能是语法错误
Q2: 中文显示乱码?
A:
- Windows:确保控制台支持UTF-8编码
- 可以在代码中添加
system("chcp 65001");设置UTF-8编码
- 可以在代码中添加
- Linux/Mac:设置环境变量
export LANG=zh_CN.UTF-8 - 或者使用支持UTF-8的终端
Q3: 数据文件无法打开?
A:
- 检查文件路径是否正确
- 确保文件没有被其他程序占用
- 检查文件权限
- 检查文件编码是否为UTF-8
Q4: 如何恢复已删除的联系人?
A:
- 当前版本使用软删除,可以通过修改源代码恢复
- 或者从备份文件中恢复数据
- 建议升级到支持回收站功能的版本
Q5: 可以导入Excel文件吗?
A:
- 可以!将Excel文件另存为CSV格式
- 确保字段顺序与系统要求一致
- 使用"加载数据文件"功能导入
- 注意:Excel导出的CSV可能需要调整编码为UTF-8
Q6: 联系人姓名可以重复吗?
A:
- 不可以。系统使用姓名作为唯一标识
- 如果添加重复姓名,系统会提示已存在
- 如果需要添加同名联系人,建议在姓名后添加标识(如:张三(同事))
Q7: 如何批量导入联系人?
A:
- 准备CSV格式的联系人数据
- 确保格式符合系统要求
- 使用"系统设置" -> "加载数据文件"功能
- 系统会自动合并数据(相同学名会覆盖)
版本历史
v1.0 (当前版本)
- 完整的联系人管理功能(增删改查)
- 支持多种查询方式(姓名、手机号、分组)
- 分组管理功能
- 数据持久化(CSV格式)
- 自动备份功能
- 数据导入导出功能
- 友好的用户界面(彩色输出、清晰菜单)
- 输入数据验证
- 详细的代码注释
许可证
本项目仅供学习和教育使用。
开发信息
- 开发语言:C语言
- 开发环境:支持Windows/Linux/Mac
- 编译器要求:GCC 或兼容的C编译器
- 标准库:C标准库(stdio.h, stdlib.h, string.h等)
学习建议
本系统适合用于:
- C语言学习项目
- 数据结构与算法实践
- 文件操作练习
- 控制台程序开发
- 字符串处理练习
可以改进的方向
-
数据结构优化:
- 使用链表或树结构支持更大数据量
- 使用哈希表提高查找效率
-
数据库集成:
- 使用SQLite替代CSV文件
- 支持更复杂的数据查询
-
图形界面:
- 开发GUI版本(如使用GTK+或Qt)
- 提供更友好的用户体验
-
网络功能:
- 支持多用户网络访问
- 数据云端同步
-
数据加密:
- 保护联系人隐私信息
- 支持密码保护
-
高级功能:
- 联系人头像支持
- 生日提醒功能
- 通话记录
- 短信记录
-
数据导入导出:
- 支持vCard格式
- 支持Excel格式
- 支持JSON格式
技术支持
如有问题或建议,请:
- 检查本文档的常见问题部分
- 查看源代码注释
- 检查数据文件格式是否正确
- 查看编译错误信息
使用技巧
-
合理使用分组:
- 建议使用统一的分组命名(如:家人、朋友、同事、同学等)
- 便于快速查找和管理
-
充分利用备注:
- 在备注中记录联系人的其他信息
- 如:生日、爱好、工作单位等
-
定期备份:
- 建议每周或每月备份一次
- 重要操作前先备份
-
数据整理:
- 定期清理无效或重复的联系人
- 保持数据的准确性和完整性
-
导出备份:
- 可以将数据导出为CSV文件
- 在Excel中查看和编辑
- 便于数据分析和整理
源代码
/*
* 通讯录管理系统 v1.0
*
* 功能说明:
* - 联系人信息管理(添加、删除、修改、查询)
* - 支持多种查询方式(按姓名、电话、分组等)
* - 数据持久化(CSV格式)
* - 数据导入导出和备份功能
*
* 编译方法:
* Windows (MinGW): gcc contact_manager.c -o contact_manager.exe
* Linux/Mac: gcc contact_manager.c -o contact_manager
*
* 作者:系统
* 日期:2024
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#endif
/* ==================== 常量定义 ==================== */
#define NAME_LEN 50 // 姓名最大长度
#define PHONE_LEN 20 // 电话最大长度
#define MOBILE_LEN 20 // 手机最大长度
#define EMAIL_LEN 50 // 邮箱最大长度
#define ADDRESS_LEN 100 // 地址最大长度
#define GROUP_LEN 30 // 分组最大长度
#define NOTE_LEN 200 // 备注最大长度
#define MAX_CONTACTS 10000 // 最大联系人数量
#define DATA_FILE "contacts.csv" // 默认数据文件
#define BACKUP_DIR "backups" // 备份目录
/* ==================== 数据结构定义 ==================== */
/**
* 联系人结构体
* 包含联系人的所有基本信息
*/
typedef struct {
char name[NAME_LEN]; // 姓名(必填)
char phone[PHONE_LEN]; // 固定电话
char mobile[MOBILE_LEN]; // 手机号码
char email[EMAIL_LEN]; // 电子邮箱
char address[ADDRESS_LEN]; // 地址
char group[GROUP_LEN]; // 分组(如:家人、朋友、同事等)
char note[NOTE_LEN]; // 备注信息
int isDeleted; // 删除标记(0=未删除,1=已删除)
} Contact;
/* ==================== 全局变量 ==================== */
Contact contacts[MAX_CONTACTS]; // 联系人数组
int contact_count = 0; // 当前联系人数量
/* ==================== 颜色控制函数(Windows平台) ==================== */
#ifdef _WIN32
/**
* 设置控制台文字颜色(仅Windows平台)
* @param attr 颜色属性
*/
void set_color(WORD attr) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attr);
}
#define COLOR_RESET (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE)
#define COLOR_RED (FOREGROUND_RED | FOREGROUND_INTENSITY)
#define COLOR_GREEN (FOREGROUND_GREEN | FOREGROUND_INTENSITY)
#define COLOR_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
#define COLOR_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
#define COLOR_BLUE (FOREGROUND_BLUE | FOREGROUND_INTENSITY)
#else
// Linux/Mac平台的颜色控制(空实现)
void set_color(int attr) { (void)attr; }
#define COLOR_RESET 0
#define COLOR_RED 0
#define COLOR_GREEN 0
#define COLOR_YELLOW 0
#define COLOR_CYAN 0
#define COLOR_BLUE 0
#endif
/* ==================== 工具函数 ==================== */
/**
* 清屏函数(跨平台)
*/
void clear_screen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
/**
* 暂停程序,等待用户按回车键
*/
void pause_anykey() {
printf("\n按回车键继续...");
getchar();
getchar();
}
/**
* 去除字符串末尾的换行符
* @param s 要处理的字符串
*/
void trim_newline(char *s) {
if (!s) return;
size_t n = strlen(s);
if (n > 0 && s[n-1] == '\n') {
s[n-1] = '\0';
}
}
/**
* 从标准输入读取一行文本
* @param buf 缓冲区
* @param sz 缓冲区大小
* @return 成功返回buf指针,失败返回NULL
*/
char* read_line(char *buf, size_t sz) {
if (!fgets(buf, (int)sz, stdin)) return NULL;
trim_newline(buf);
return buf;
}
/**
* 交互式读取整数输入
* @param prompt 提示信息
* @return 读取到的整数值
*/
int read_int(const char *prompt) {
char buf[64];
while (1) {
printf("%s", prompt);
if (!read_line(buf, sizeof(buf))) return 0;
if (strlen(buf) == 0) return 0;
char *end;
long v = strtol(buf, &end, 10);
if (end != buf && *end == '\0') return (int)v;
set_color(COLOR_RED);
printf("输入无效,请输入整数。\n");
set_color(COLOR_RESET);
}
}
/**
* 验证姓名是否有效(不能为空)
* @param name 姓名
* @return 有效返回1,无效返回0
*/
int validate_name(const char *name) {
if (strlen(name) == 0 || strlen(name) > NAME_LEN - 1) return 0;
return 1;
}
/**
* 验证电话号码格式(简单验证:只检查是否为空和长度)
* @param phone 电话号码
* @return 有效返回1,无效返回0
*/
int validate_phone(const char *phone) {
if (strlen(phone) > PHONE_LEN - 1) return 0;
// 可以添加更复杂的验证逻辑,如检查是否全为数字等
return 1;
}
/**
* 验证邮箱格式(简单验证)
* @param email 邮箱地址
* @return 有效返回1,无效返回0
*/
int validate_email(const char *email) {
if (strlen(email) == 0) return 1; // 邮箱可以为空
if (strlen(email) > EMAIL_LEN - 1) return 0;
// 简单检查是否包含@符号
if (strchr(email, '@') == NULL && strlen(email) > 0) return 0;
return 1;
}
/**
* 查找联系人(按姓名精确匹配)
* @param name 要查找的姓名
* @return 找到返回索引,未找到返回-1
*/
int find_by_name(const char *name) {
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted == 0 && strcmp(contacts[i].name, name) == 0) {
return i;
}
}
return -1;
}
/**
* 查找联系人(按手机号精确匹配)
* @param mobile 要查找的手机号
* @return 找到返回索引,未找到返回-1
*/
int find_by_mobile(const char *mobile) {
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted == 0 &&
strlen(mobile) > 0 &&
strcmp(contacts[i].mobile, mobile) == 0) {
return i;
}
}
return -1;
}
/**
* 显示加载动画
*/
void show_loading() {
printf("\n");
for (int i = 0; i <= 20; i++) {
printf("\r系统加载中: [");
for (int j = 0; j < 20; j++) {
if (j < i) printf("=");
else printf(" ");
}
printf("] %.0f%%", i * 5.0);
fflush(stdout);
#ifdef _WIN32
Sleep(50);
#else
usleep(50000);
#endif
}
printf("\n");
clear_screen();
}
/**
* 打印分隔线
*/
void print_separator() {
printf("===========================================================\n");
}
/**
* 打印标题
* @param title 标题文本
*/
void print_title(const char *title) {
print_separator();
printf(" %s\n", title);
print_separator();
}
/* ==================== 数据持久化函数 ==================== */
/**
* 保存数据到CSV文件
* @param filename 文件名
* @return 成功返回1,失败返回0
*/
int save_data(const char *filename) {
FILE *f = fopen(filename, "w");
if (!f) {
set_color(COLOR_RED);
perror("打开文件写入失败");
set_color(COLOR_RESET);
return 0;
}
// 写入CSV表头
fprintf(f, "姓名,固定电话,手机,邮箱,地址,分组,备注\n");
// 写入所有联系人数据
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted) continue; // 跳过已删除的记录
Contact *c = &contacts[i];
// 使用引号包裹可能包含逗号的字段
fprintf(f, "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
c->name, c->phone, c->mobile, c->email,
c->address, c->group, c->note);
}
fclose(f);
return 1;
}
/**
* 从CSV文件加载数据
* @param filename 文件名
* @return 成功返回1,失败返回0
*/
int load_data(const char *filename) {
FILE *f = fopen(filename, "r");
if (!f) return 0; // 文件不存在视为正常情况
char line[1024];
int line_no = 0;
contact_count = 0;
while (fgets(line, sizeof(line), f) && contact_count < MAX_CONTACTS) {
line_no++;
trim_newline(line);
if (line[0] == '\0') continue;
// 跳过CSV表头
if (line_no == 1 && (strstr(line, "姓名") != NULL || strstr(line, "name") != NULL)) {
continue;
}
// 解析CSV行(简单解析,支持引号包裹的字段)
Contact c;
memset(&c, 0, sizeof(c));
// 简单的CSV解析:按逗号分割,去除引号
char *p = line;
int field = 0;
char *start = p;
int in_quotes = 0;
while (*p && field < 7) {
if (*p == '"') {
in_quotes = !in_quotes;
} else if (*p == ',' && !in_quotes) {
*p = '\0';
// 去除字段两端的引号和空格
while (*start == '"' || *start == ' ') start++;
char *end = p - 1;
while (end > start && (*end == '"' || *end == ' ')) end--;
*(end + 1) = '\0';
// 根据字段索引赋值
switch (field) {
case 0: strncpy(c.name, start, NAME_LEN - 1); break;
case 1: strncpy(c.phone, start, PHONE_LEN - 1); break;
case 2: strncpy(c.mobile, start, MOBILE_LEN - 1); break;
case 3: strncpy(c.email, start, EMAIL_LEN - 1); break;
case 4: strncpy(c.address, start, ADDRESS_LEN - 1); break;
case 5: strncpy(c.group, start, GROUP_LEN - 1); break;
case 6: strncpy(c.note, start, NOTE_LEN - 1); break;
}
field++;
start = p + 1;
}
p++;
}
// 处理最后一个字段
if (field < 7 && *start) {
while (*start == '"' || *start == ' ') start++;
char *end = p - 1;
while (end > start && (*end == '"' || *end == ' ' || *end == '\n')) end--;
*(end + 1) = '\0';
switch (field) {
case 6: strncpy(c.note, start, NOTE_LEN - 1); break;
}
}
// 验证数据有效性
if (!validate_name(c.name)) continue;
c.isDeleted = 0;
// 检查是否已存在(按姓名判断)
int idx = find_by_name(c.name);
if (idx != -1) {
// 覆盖已有记录
contacts[idx] = c;
} else {
// 添加新记录
contacts[contact_count++] = c;
}
}
fclose(f);
return 1;
}
/**
* 备份数据文件
* @return 成功返回1,失败返回0
*/
int backup_data() {
time_t t = time(NULL);
struct tm *tm = localtime(&t);
char backup_file[256];
// 创建备份目录(如果不存在)
#ifdef _WIN32
system("if not exist backups mkdir backups");
#else
system("mkdir -p backups");
#endif
// 生成带时间戳的备份文件名
snprintf(backup_file, sizeof(backup_file),
"%s/backup_%04d%02d%02d_%02d%02d%02d.csv",
BACKUP_DIR,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
if (save_data(backup_file)) {
set_color(COLOR_GREEN);
printf("备份成功: %s\n", backup_file);
set_color(COLOR_RESET);
return 1;
} else {
set_color(COLOR_RED);
printf("备份失败\n");
set_color(COLOR_RESET);
return 0;
}
}
/* ==================== 联系人管理函数 ==================== */
/**
* 添加联系人
*/
void add_contact() {
if (contact_count >= MAX_CONTACTS) {
set_color(COLOR_RED);
printf("联系人数量已达上限 (%d)\n", MAX_CONTACTS);
set_color(COLOR_RESET);
return;
}
print_title("添加联系人");
Contact c;
memset(&c, 0, sizeof(c));
char buf[256];
// 输入姓名(必填)
while (1) {
printf("姓名*: ");
if (!read_line(buf, sizeof(buf))) return;
if (!validate_name(buf)) {
set_color(COLOR_RED);
printf("姓名不能为空,请重新输入\n");
set_color(COLOR_RESET);
continue;
}
// 检查是否已存在
if (find_by_name(buf) != -1) {
set_color(COLOR_RED);
printf("该姓名已存在,请使用修改功能或使用不同姓名\n");
set_color(COLOR_RESET);
continue;
}
strncpy(c.name, buf, NAME_LEN - 1);
break;
}
// 输入固定电话(可选)
printf("固定电话: ");
read_line(buf, sizeof(buf));
if (validate_phone(buf)) {
strncpy(c.phone, buf, PHONE_LEN - 1);
}
// 输入手机号码(可选)
printf("手机号码: ");
read_line(buf, sizeof(buf));
if (validate_phone(buf)) {
strncpy(c.mobile, buf, MOBILE_LEN - 1);
}
// 输入邮箱(可选)
while (1) {
printf("电子邮箱: ");
read_line(buf, sizeof(buf));
if (validate_email(buf)) {
strncpy(c.email, buf, EMAIL_LEN - 1);
break;
} else if (strlen(buf) == 0) {
break; // 允许为空
} else {
set_color(COLOR_RED);
printf("邮箱格式不正确,请重新输入(或留空跳过)\n");
set_color(COLOR_RESET);
}
}
// 输入地址(可选)
printf("地址: ");
read_line(buf, sizeof(buf));
strncpy(c.address, buf, ADDRESS_LEN - 1);
// 输入分组(可选)
printf("分组 (如:家人、朋友、同事等): ");
read_line(buf, sizeof(buf));
strncpy(c.group, buf, GROUP_LEN - 1);
// 输入备注(可选)
printf("备注: ");
read_line(buf, sizeof(buf));
strncpy(c.note, buf, NOTE_LEN - 1);
c.isDeleted = 0;
// 添加到数组
contacts[contact_count++] = c;
set_color(COLOR_GREEN);
printf("\n添加成功!\n");
set_color(COLOR_RESET);
}
/**
* 删除联系人
*/
void delete_contact() {
print_title("删除联系人");
char name[NAME_LEN];
printf("输入要删除的联系人姓名: ");
read_line(name, sizeof(name));
int idx = find_by_name(name);
if (idx == -1) {
set_color(COLOR_RED);
printf("未找到联系人: %s\n", name);
set_color(COLOR_RESET);
return;
}
Contact *c = &contacts[idx];
printf("\n找到联系人:\n");
printf("姓名: %s\n", c->name);
if (strlen(c->phone) > 0) printf("固定电话: %s\n", c->phone);
if (strlen(c->mobile) > 0) printf("手机: %s\n", c->mobile);
if (strlen(c->email) > 0) printf("邮箱: %s\n", c->email);
printf("\n确认删除? (Y/N): ");
char ans[8];
read_line(ans, sizeof(ans));
if (ans[0] == 'Y' || ans[0] == 'y') {
c->isDeleted = 1; // 软删除
set_color(COLOR_GREEN);
printf("删除成功\n");
set_color(COLOR_RESET);
} else {
printf("已取消\n");
}
}
/**
* 修改联系人信息
*/
void modify_contact() {
print_title("修改联系人");
char name[NAME_LEN];
printf("输入要修改的联系人姓名: ");
read_line(name, sizeof(name));
int idx = find_by_name(name);
if (idx == -1) {
set_color(COLOR_RED);
printf("未找到联系人: %s\n", name);
set_color(COLOR_RESET);
return;
}
Contact *c = &contacts[idx];
set_color(COLOR_CYAN);
printf("\n找到联系人: %s\n", c->name);
set_color(COLOR_RESET);
char buf[256];
while (1) {
printf("\n选择要修改的字段:\n");
printf("1. 姓名\n");
printf("2. 固定电话\n");
printf("3. 手机号码\n");
printf("4. 电子邮箱\n");
printf("5. 地址\n");
printf("6. 分组\n");
printf("7. 备注\n");
printf("0. 完成并返回\n");
int choice = read_int("\n选择 (0-7): ");
if (choice == 0) break;
switch (choice) {
case 1: {
printf("当前姓名: %s\n", c->name);
printf("新姓名: ");
read_line(buf, sizeof(buf));
if (strlen(buf) > 0 && validate_name(buf)) {
// 检查新姓名是否与其他联系人冲突
int other_idx = find_by_name(buf);
if (other_idx != -1 && other_idx != idx) {
set_color(COLOR_RED);
printf("该姓名已存在,无法修改\n");
set_color(COLOR_RESET);
} else {
strncpy(c->name, buf, NAME_LEN - 1);
}
}
break;
}
case 2:
printf("当前固定电话: %s\n", c->phone);
printf("新固定电话: ");
read_line(buf, sizeof(buf));
if (validate_phone(buf)) {
strncpy(c->phone, buf, PHONE_LEN - 1);
}
break;
case 3:
printf("当前手机号码: %s\n", c->mobile);
printf("新手机号码: ");
read_line(buf, sizeof(buf));
if (validate_phone(buf)) {
strncpy(c->mobile, buf, MOBILE_LEN - 1);
}
break;
case 4:
printf("当前电子邮箱: %s\n", c->email);
while (1) {
printf("新电子邮箱: ");
read_line(buf, sizeof(buf));
if (validate_email(buf)) {
strncpy(c->email, buf, EMAIL_LEN - 1);
break;
} else if (strlen(buf) == 0) {
break; // 允许清空
} else {
set_color(COLOR_RED);
printf("邮箱格式不正确,请重新输入(或留空清空)\n");
set_color(COLOR_RESET);
}
}
break;
case 5:
printf("当前地址: %s\n", c->address);
printf("新地址: ");
read_line(buf, sizeof(buf));
strncpy(c->address, buf, ADDRESS_LEN - 1);
break;
case 6:
printf("当前分组: %s\n", c->group);
printf("新分组: ");
read_line(buf, sizeof(buf));
strncpy(c->group, buf, GROUP_LEN - 1);
break;
case 7:
printf("当前备注: %s\n", c->note);
printf("新备注: ");
read_line(buf, sizeof(buf));
strncpy(c->note, buf, NOTE_LEN - 1);
break;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
break;
}
}
set_color(COLOR_GREEN);
printf("\n修改完成\n");
set_color(COLOR_RESET);
}
/**
* 显示所有联系人
*/
void show_all_contacts() {
print_title("所有联系人");
int count = 0;
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted == 0) count++;
}
if (count == 0) {
printf("暂无联系人数据\n");
return;
}
printf("共 %d 个联系人\n\n", count);
// 打印表头
printf("%-15s %-15s %-15s %-20s %-15s\n",
"姓名", "固定电话", "手机", "邮箱", "分组");
print_separator();
// 打印所有联系人
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted) continue;
Contact *c = &contacts[i];
printf("%-15s %-15s %-15s %-20s %-15s\n",
c->name,
strlen(c->phone) > 0 ? c->phone : "-",
strlen(c->mobile) > 0 ? c->mobile : "-",
strlen(c->email) > 0 ? c->email : "-",
strlen(c->group) > 0 ? c->group : "-");
}
}
/**
* 显示联系人详细信息
* @param c 联系人指针
*/
void show_contact_detail(Contact *c) {
if (!c) return;
printf("\n========================================\n");
printf("姓名: %s\n", c->name);
if (strlen(c->phone) > 0) {
printf("固定电话: %s\n", c->phone);
}
if (strlen(c->mobile) > 0) {
printf("手机号码: %s\n", c->mobile);
}
if (strlen(c->email) > 0) {
printf("电子邮箱: %s\n", c->email);
}
if (strlen(c->address) > 0) {
printf("地址: %s\n", c->address);
}
if (strlen(c->group) > 0) {
printf("分组: %s\n", c->group);
}
if (strlen(c->note) > 0) {
printf("备注: %s\n", c->note);
}
printf("========================================\n");
}
/* ==================== 查询功能函数 ==================== */
/**
* 按姓名查询(支持模糊匹配)
*/
void query_by_name() {
print_title("按姓名查询");
char key[NAME_LEN];
printf("输入姓名关键字: ");
read_line(key, sizeof(key));
int found = 0;
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted) continue;
if (strstr(contacts[i].name, key) != NULL) {
if (!found) {
printf("\n查询结果:\n");
}
show_contact_detail(&contacts[i]);
found = 1;
}
}
if (!found) {
set_color(COLOR_RED);
printf("未找到匹配的联系人\n");
set_color(COLOR_RESET);
}
}
/**
* 按手机号查询
*/
void query_by_mobile() {
print_title("按手机号查询");
char mobile[MOBILE_LEN];
printf("输入手机号码: ");
read_line(mobile, sizeof(mobile));
int idx = find_by_mobile(mobile);
if (idx == -1) {
set_color(COLOR_RED);
printf("未找到该手机号对应的联系人\n");
set_color(COLOR_RESET);
return;
}
printf("\n查询结果:\n");
show_contact_detail(&contacts[idx]);
}
/**
* 按分组查询
*/
void query_by_group() {
print_title("按分组查询");
char group[GROUP_LEN];
printf("输入分组名称: ");
read_line(group, sizeof(group));
int found = 0;
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted) continue;
if (strcmp(contacts[i].group, group) == 0) {
if (!found) {
printf("\n查询结果:\n");
}
show_contact_detail(&contacts[i]);
found = 1;
}
}
if (!found) {
set_color(COLOR_RED);
printf("未找到该分组的联系人\n");
set_color(COLOR_RESET);
}
}
/**
* 显示所有分组
*/
void show_all_groups() {
print_title("所有分组");
// 收集所有不重复的分组
char groups[100][GROUP_LEN];
int group_count = 0;
for (int i = 0; i < contact_count; i++) {
if (contacts[i].isDeleted) continue;
if (strlen(contacts[i].group) == 0) continue;
int found = 0;
for (int j = 0; j < group_count; j++) {
if (strcmp(contacts[i].group, groups[j]) == 0) {
found = 1;
break;
}
}
if (!found && group_count < 100) {
strncpy(groups[group_count], contacts[i].group, GROUP_LEN - 1);
group_count++;
}
}
if (group_count == 0) {
printf("暂无分组信息\n");
return;
}
printf("共 %d 个分组:\n\n", group_count);
for (int i = 0; i < group_count; i++) {
// 统计该分组的人数
int count = 0;
for (int j = 0; j < contact_count; j++) {
if (contacts[j].isDeleted == 0 &&
strcmp(contacts[j].group, groups[i]) == 0) {
count++;
}
}
printf("%d. %s (%d人)\n", i + 1, groups[i], count);
}
}
/* ==================== 菜单系统 ==================== */
/**
* 联系人管理菜单
*/
void contact_management_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("联系人管理");
printf("1. 添加联系人\n");
printf("2. 删除联系人\n");
printf("3. 修改联系人\n");
printf("4. 显示所有联系人\n");
printf("0. 返回主菜单\n");
int c = read_int("\n请选择操作 (0-4): ");
switch (c) {
case 1: add_contact(); pause_anykey(); break;
case 2: delete_contact(); pause_anykey(); break;
case 3: modify_contact(); pause_anykey(); break;
case 4: show_all_contacts(); pause_anykey(); break;
case 0: return;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/**
* 查询菜单
*/
void query_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("数据查询");
printf("1. 按姓名查询\n");
printf("2. 按手机号查询\n");
printf("3. 按分组查询\n");
printf("4. 显示所有分组\n");
printf("0. 返回主菜单\n");
int c = read_int("\n请选择操作 (0-4): ");
switch (c) {
case 1: query_by_name(); pause_anykey(); break;
case 2: query_by_mobile(); pause_anykey(); break;
case 3: query_by_group(); pause_anykey(); break;
case 4: show_all_groups(); pause_anykey(); break;
case 0: return;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/**
* 系统设置菜单
*/
void settings_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("系统设置");
printf("1. 保存数据\n");
printf("2. 备份数据\n");
printf("3. 加载数据文件\n");
printf("4. 导出CSV文件\n");
printf("0. 返回主菜单\n");
int c = read_int("\n请选择操作 (0-4): ");
switch (c) {
case 1:
if (save_data(DATA_FILE)) {
set_color(COLOR_GREEN);
printf("保存成功\n");
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("保存失败\n");
set_color(COLOR_RESET);
}
pause_anykey();
break;
case 2:
backup_data();
pause_anykey();
break;
case 3: {
char fname[260];
printf("输入要加载的文件名 (默认 %s): ", DATA_FILE);
read_line(fname, sizeof(fname));
if (strlen(fname) == 0) strcpy(fname, DATA_FILE);
if (load_data(fname)) {
set_color(COLOR_GREEN);
printf("加载成功,共 %d 条记录\n", contact_count);
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("加载失败或文件不存在\n");
set_color(COLOR_RESET);
}
pause_anykey();
break;
}
case 4: {
char fname[260];
printf("导出文件名 (默认 %s): ", DATA_FILE);
read_line(fname, sizeof(fname));
if (strlen(fname) == 0) strcpy(fname, DATA_FILE);
if (save_data(fname)) {
set_color(COLOR_GREEN);
printf("导出成功: %s\n", fname);
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("导出失败\n");
set_color(COLOR_RESET);
}
pause_anykey();
break;
}
case 0: return;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/**
* 主菜单
*/
int main_menu() {
while (1) {
clear_screen();
#ifdef _WIN32
system("color 0B");
#endif
print_title("通讯录管理系统 v1.0");
printf("1. 联系人管理\n");
printf("2. 数据查询\n");
printf("3. 系统设置\n");
printf("0. 退出系统\n");
int c = read_int("\n请选择操作 (0-3): ");
switch (c) {
case 1: contact_management_menu(); break;
case 2: query_menu(); break;
case 3: settings_menu(); break;
case 0: return 0;
default:
set_color(COLOR_RED);
printf("无效选择\n");
set_color(COLOR_RESET);
pause_anykey();
break;
}
}
}
/* ==================== 主函数 ==================== */
/**
* 程序入口
* @return 程序退出码
*/
int main() {
#ifdef _WIN32
system("color 0B");
#endif
// 显示加载动画
show_loading();
// 尝试加载数据文件
if (load_data(DATA_FILE)) {
set_color(COLOR_GREEN);
printf("已加载数据文件 %s,共 %d 条记录\n", DATA_FILE, contact_count);
set_color(COLOR_RESET);
} else {
set_color(COLOR_YELLOW);
printf("未找到数据文件,使用空数据\n");
set_color(COLOR_RESET);
}
pause_anykey();
// 进入主菜单
main_menu();
// 退出前询问是否保存
printf("\n是否保存数据到 %s ? (Y/N): ", DATA_FILE);
char ans[8];
read_line(ans, sizeof(ans));
if (ans[0] == 'Y' || ans[0] == 'y') {
if (save_data(DATA_FILE)) {
set_color(COLOR_GREEN);
printf("保存成功\n");
set_color(COLOR_RESET);
} else {
set_color(COLOR_RED);
printf("保存失败\n");
set_color(COLOR_RESET);
}
}
printf("\n感谢使用通讯录管理系统!\n");
return 0;
}
祝您使用愉快!

15万+

被折叠的 条评论
为什么被折叠?



