前言

        在软件开发中,数据持久化是一个至关重要的功能。今天我们要分析的是一个基于顺序表实现的C语言通讯录程序,它不仅具备了基本的联系人管理功能,还实现了完整的数据持久化机制。这意味着用户的通讯录数据可以在程序关闭后依然保存,下次启动时自动恢复,大大提升了程序的实用性。在本篇文章之前已经用顺序表实现了通讯录,链接如下:基于顺序表的通讯录系统设计与实现-CSDN博客,这次是加入了通讯录的文件导入和保存。

目录

前言

正文

1. 整体架构设计

2. 数据持久化实现

数据加载机制

数据保存机制

3. 初始化与销毁流程

智能初始化

安全的销毁流程

4. 顺序表核心实现

动态容量管理

高效的数据访问

5. 业务逻辑实现

联系人查找功能

联系人删除操作

联系人修改功能

6. 用户界面设计

清晰的菜单系统

主程序循环

7. 文件格式设计

总结

技术亮点

性能分析

实际应用价值

扩展建议


正文

1. 整体架构设计

该通讯录程序采用经典的三层架构:

  • SeqList层:底层数据结构,提供动态数组管理

  • Contact层:业务逻辑层,处理通讯录的具体操作

  • Application层:用户界面层,提供交互接口

这种分层设计使得代码结构清晰,各模块职责明确,便于维护和扩展。

2. 数据持久化实现

数据加载机制
void LoadContact(Contact* con)
{
    FILE* pf = fopen("contact.txt", "rb");
    if (pf == NULL)
    {
        perror("fopen error!\n");
        return;
    }

    //循环读取文件数据
    peoInfo info;
    while (fread(&info, sizeof(info), 1, pf))
    {
        SLPushBack(con, info);
    }
    printf("历史数据导入成功!\n");
}

技术特点:

  • 使用二进制读取模式("rb"),高效直接

  • 循环读取直到文件末尾,自动处理任意数量的联系人

  • 文件不存在时给出友好提示,不会导致程序崩溃

  • 无缝集成到现有架构中,使用SLPushBack函数添加数据

数据保存机制
void SaveContact(Contact* con)
{
    FILE* pf = fopen("contact.txt", "wb");
    if (pf == NULL)
    {
        perror("fopen error!\n");
        return;
    }

    //将通讯录数据写入文件
    for (int i = 0; i < con->size; i++)
    {
        fwrite(&(con->arr[i]), sizeof(peoInfo), 1, pf);
    }
    printf("通讯录数据保存成功!\n");
}

技术特点:

  • 使用二进制写入模式("wb"),保证数据格式一致

  • 遍历整个顺序表,确保所有数据都被保存

  • 覆盖式写入,保证文件内容与内存数据完全同步

  • 明确的成功提示,让用户安心退出

3. 初始化与销毁流程

智能初始化
void ContactInit(Contact* con)
{
    SLInit(con);
    LoadContact(con);
}

初始化过程分为两步:

  1. 调用SLInit初始化顺序表结构

  2. 调用LoadContact加载历史数据

这种设计确保了程序启动时能够自动恢复之前的工作状态。

安全的销毁流程
void ContactDesTroy(Contact* con)
{
    SaveContact(con);
    SLDestroy(con);
}

销毁过程同样分为两步:

  1. 先保存数据到文件

  2. 再释放内存资源

这个顺序至关重要,确保了数据不会丢失。

4. 顺序表核心实现

动态容量管理
void SLCheckCapacity(SL* ps)
{
    if (ps->capacity == ps->size)
    {
        int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        SLDataType* tmp = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));
        if (tmp == NULL)
        {
            perror("realloc fail!");
            exit(1);
        }
        ps->arr = tmp;
        ps->capacity = newCapacity;
    }
}

扩容策略分析:

  • 初始容量:4个元素,避免过度分配

  • 扩容倍数:每次翻倍,保证均摊时间复杂度为O(1)

  • 错误处理:内存分配失败时安全退出

  • 使用realloc:智能处理内存重新分配

高效的数据访问
void ContactShow(Contact* con)
{
    printf("%-10s %-6s %-6s %-15s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
    for (int i = 0; i < con->size; i++)
    {
        printf("%-10s %-6s %-6d %-15s %-20s\n",
            con->arr[i].name,
            con->arr[i].gender,
            con->arr[i].age,
            con->arr[i].tel,
            con->arr[i].addr);
    }
}

显示优化:

  • 使用格式化输出,保证列对齐

  • 左对齐显示,提高可读性

  • 直接通过索引访问,时间复杂度O(1)

5. 业务逻辑实现

联系人查找功能
int FindByName(Contact* con, char name[])
{
    for (int i = 0; i < con->size; i++)
    {
        if (0 == strcmp(con->arr[i].name, name))
        {
            return i;
        }
    }
    return -1;
}

查找策略:

  • 顺序查找,时间复杂度O(n)

  • 使用strcmp进行精确字符串匹配

  • 返回索引位置,便于后续操作

联系人删除操作
void ContactDel(Contact* con)
{
    char name[NAME_MAX];
    printf("请输入要删除的联系人姓名:\n");
    scanf("%s", name);
    int find = FindByName(con, name);
    if (find < 0)
    {
        printf("要删除的联系人数据不存在\n");
        return;
    }
    SLErase(con, find);
    printf("删除成功!\n");
}

删除流程:

  1. 通过姓名查找联系人

  2. 验证联系人是否存在

  3. 调用SLErase执行删除操作

  4. 提供明确的用户反馈

联系人修改功能
void ContactModify(Contact* con)
{
    char name[NAME_MAX];
    printf("请输入要修改的联系人姓名:\n");
    scanf("%s", name);
    int find = FindByName(con, name);
    if (find < 0)
    {
        printf("要删除的联系人数据不存在\n");
        return;
    }
    // 直接修改数组中的元素
    printf("请输入新的联系人的姓名:\n");
    scanf("%s", con->arr[find].name);
    // ... 其他字段修改
    printf("修改成功!\n");
}

修改特点:

  • 直接通过索引修改数组元素,效率高

  • 原地修改,无需移动其他数据

  • 提供完整的字段修改支持

6. 用户界面设计

清晰的菜单系统
void menu()
{
    printf("******************************************\n");
    printf("*********         通讯录         *********\n");
    printf("********1.添加联系人    2.删除联系人******\n");
    printf("********3.修改联系人    4.查找联系人******\n");
    printf("********5.展示联系人    0.退出通讯录******\n");
    printf("******************************************\n");
}

菜单设计简洁明了,功能划分清晰,用户可以直观地选择所需操作。

主程序循环
int main()
{
    Contact con;
    ContactInit(&con);
    int n = 0;

    do
    {
        menu();
        printf("请输入你的选择:\n");
        scanf("%d", &n);
        switch (n)
        {
        case 1:
            ContactAdd(&con);
            break;
        // 其他功能分支...
        case 0:
            printf("退出通讯录!\n");
            break;
        default:
            printf("输入错误,请重新输入:\n");
            break;
        }
    } while (n);
    ContactDesTroy(&con);
    return 0;
}

程序流程特点:

  • 自动初始化,加载历史数据

  • 循环菜单,支持连续操作

  • 安全的退出处理,自动保存数据

  • 完善的错误输入处理

7. 文件格式设计

程序采用二进制格式存储数据,具有显著优势:

// 写入:直接内存拷贝
fwrite(&(con->arr[i]), sizeof(peoInfo), 1, pf);

// 读取:直接内存填充  
fread(&info, sizeof(info), 1, pf)

二进制格式的优点:

  • 高性能:无需数据格式转换

  • 完整性:保持所有数据类型原样

  • 紧凑性:相比文本格式更节省空间

  • 一致性:读写操作完全对称

总结

技术亮点

  1. 完整的数据持久化:实现了数据的自动保存和加载

  2. 健壮的错误处理:文件操作和内存分配都有完善的错误处理

  3. 高效的数据结构:顺序表提供O(1)的随机访问能力

  4. 用户友好的界面:清晰的操作提示和反馈

  5. 模块化设计:各层职责明确,便于维护和扩展

性能分析

优势:

  • 随机访问:O(1)时间复杂度

  • 缓存友好:连续内存布局

  • 尾插操作:均摊O(1)时间复杂度

  • 数据展示:高效的顺序遍历

劣势:

  • 插入删除:平均需要移动O(n)个元素

  • 内存管理:扩容时可能需要数据复制

实际应用价值

这个通讯录程序已经具备了实际使用的基本条件:

  • 数据安全:自动保存确保数据不丢失

  • 操作简便:直观的菜单和提示

  • 性能良好:能够快速处理常见操作

  • 稳定可靠:完善的错误处理机制

扩展建议

虽然当前版本功能完整,但仍可进一步改进:

  1. 增量保存:每次修改后立即保存,避免退出时数据丢失风险

  2. 数据备份:支持多个通讯录文件和版本管理

  3. 搜索优化:实现按电话号码、地址等多条件查询

  4. 数据验证:添加输入数据的有效性检查

  5. 批量操作:支持联系人的批量导入导出

这个基于顺序表的通讯录实现不仅是一个优秀的数据结构教学示例,更是一个具备完整数据持久化功能的实用工具,很好地展示了C语言在系统编程和文件操作方面的强大能力。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐