C语言通讯录管理系统 - 完整开发指南与源码解析,附带源码

项目简介

这是一个功能完善的通讯录管理系统,使用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 删除联系人
  • 功能:删除指定联系人的信息
  • 操作流程
    1. 输入要删除的联系人姓名
    2. 系统显示联系人信息供确认
    3. 输入 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字符

数据导入注意事项

  1. 文件编码:建议使用UTF-8编码,避免中文乱码
  2. 表头:第一行必须是表头,系统会自动识别
  3. 字段顺序:必须按照上述顺序排列
  4. 数据验证
    • 姓名不能为空
    • 相同学名的数据会覆盖已有记录
    • 邮箱格式会进行简单验证
  5. 特殊字符:字段中包含逗号或引号时,系统会自动处理

? 使用示例

示例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;

核心算法

  1. 数据查找

    • 线性查找:O(n)
    • 按姓名查找:精确匹配和模糊匹配
    • 按手机号查找:精确匹配
    • 适用于中小规模数据(<10000条)
  2. 数据存储

    • 使用静态数组存储,最大容量10000条
    • CSV格式持久化,便于查看和编辑
    • 支持软删除(isDeleted标记)
  3. 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"  // 默认数据文件

自定义字段

如需添加新字段:

  1. Contact 结构体中添加新字段
  2. add_contact() 函数中添加输入逻辑
  3. modify_contact() 函数中添加修改逻辑
  4. save_data()load_data() 函数中处理新字段
  5. 重新编译程序

? 注意事项

数据安全

  1. 定期备份:建议定期使用备份功能保存数据
  2. 数据验证:系统会对输入数据进行基本验证,但仍需谨慎输入
  3. 文件权限:确保程序对数据文件有读写权限
  4. 文件编码:使用UTF-8编码,避免中文乱码

使用建议

  1. 首次使用:建议先添加少量测试数据,熟悉操作流程
  2. 分组管理:合理使用分组功能,便于分类管理联系人
  3. 定期保存:修改数据后及时保存,避免数据丢失
  4. 备份策略:建议在重要操作前先备份数据

已知限制

  1. 数据规模:最大支持10000条联系人记录
  2. 并发访问:不支持多用户同时访问同一数据文件
  3. CSV解析:使用简单的CSV解析,不支持复杂的转义字符
  4. 邮箱验证:邮箱格式验证较简单,仅检查是否包含@符号
  5. 电话号码验证:电话号码格式验证较简单,仅检查长度

常见问题

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语言学习项目
  • 数据结构与算法实践
  • 文件操作练习
  • 控制台程序开发
  • 字符串处理练习

可以改进的方向

  1. 数据结构优化

    • 使用链表或树结构支持更大数据量
    • 使用哈希表提高查找效率
  2. 数据库集成

    • 使用SQLite替代CSV文件
    • 支持更复杂的数据查询
  3. 图形界面

    • 开发GUI版本(如使用GTK+或Qt)
    • 提供更友好的用户体验
  4. 网络功能

    • 支持多用户网络访问
    • 数据云端同步
  5. 数据加密

    • 保护联系人隐私信息
    • 支持密码保护
  6. 高级功能

    • 联系人头像支持
    • 生日提醒功能
    • 通话记录
    • 短信记录
  7. 数据导入导出

    • 支持vCard格式
    • 支持Excel格式
    • 支持JSON格式

技术支持

如有问题或建议,请:

  1. 检查本文档的常见问题部分
  2. 查看源代码注释
  3. 检查数据文件格式是否正确
  4. 查看编译错误信息

使用技巧

  1. 合理使用分组

    • 建议使用统一的分组命名(如:家人、朋友、同事、同学等)
    • 便于快速查找和管理
  2. 充分利用备注

    • 在备注中记录联系人的其他信息
    • 如:生日、爱好、工作单位等
  3. 定期备份

    • 建议每周或每月备份一次
    • 重要操作前先备份
  4. 数据整理

    • 定期清理无效或重复的联系人
    • 保持数据的准确性和完整性
  5. 导出备份

    • 可以将数据导出为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;
}



祝您使用愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员春至

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值