C语言之通讯录(动态 存储文件版)

简介: C语言之通讯录(动态 存储文件版)

前言


文件存储版的通讯录 是在通讯录的基础上添加了文件操作

在了解通讯录(文件存储版)之前,我们得好好学习一下  文件操作

只有掌握了操作文件,才能明白文件存储版的通讯录

一.基本思路


      1.通讯录是由多人的信息组合,信息:姓名,年龄,性别,地址等。

      2.通讯录的大小,存放的人数,此使用动态存储,更加方便存储和利用空间。

     3.通讯录的基本功能:增加联系人、删除联系人、查找联系人,修改联系人

      4.保存通讯录文档,方便下次使用。


注:通讯录分三种:静态、动态、文件三种。而此文章讲述的是动态存储的文件版通讯录。


          静态:大小固定,存储的人数有明确限制,无法改变,使用数组实现。


          动态:存储人数可以调节,可以随着人数的增加而增加,选择一个初始大小,之后可进行扩充操作,  可更好的利用空间。


          文件:该通讯录是在以上两种通讯录之一上加上存储文件的操作,在程序执行结束后都无法保存,录入的信息在程序结束时就会消失。为了保存录入的信息,可以通过文件操作来实现。


二.代码的实现


 2.1通讯录菜单


                 菜单能够实现和用户的交互。需要选择增、删、查、改的功能。

                所以,通讯录需要一个菜单。

               代码如下:


void menu()
{
  printf("*********************************\n");
  printf("*******1.添加        2.删除*******\n");
  printf("*******3.查找        4.修改*******\n");
  printf("*******5.显示        6.保存*******\n");
  printf("*******0.退出              *******\n");
  printf("*********************************\n");
}
//使用函数指针数组
void (*(p[7]))(Contact*) = { Exit,AddContact,DeleteContact,SearchContact,ModifyContact,PrintContact,SaveContact };
int main()
{ 
    //Contact cl;    这是下面代码结构体的
    //InitContact(&cl);
    int input;
  do {
    menu();
    printf("请输入->");
    scanf("%d", &input);
    if (input <= 6)
      p[input](&cl);  
    else
      printf("输入错误\n");
  }while (input != 0);
}


为了方便,我们使用了函数指针数组,void (*(p[7]))(Contact*)  


2.2通讯录的定义及功能


通讯录需要姓名、性别、年龄及电话,此时我们需要使用结构体来定义。还需要定义函数:增、删、查、改、打印学生信息、保存通讯录。

            代码如下:

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<string.h>
typedef struct PeoInfo
{
  char name[10];//姓名
  int age;      //年龄
  char sex[3];  //性别
  int phone[12];//电话
}PeoInfo;
typedef struct Contact
{ 
  int size;  //当前存储的人数
  int capacity;  //通讯录容量大小
  contact* data;  //结构体指针,访问个人信息
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//删除联系人
void DeleteContact(Contact* pc);
//查找联系人
void SearchContact(Contact* pc);
//修改联系人
void ModifyContact(Contact* pc);
//显示通讯录
void PrintContact(Contact* pc);
//扩容通讯录
void CheckCapacity(Contact* pc);
//保存信息到文件
void SaveContact(Contact* pc);
//文件信息传递到通讯录
void LoadContact(Contact* pc);
//销毁空间,退出程序
void Exit(Contact* pc);


2.3函数实现


注:当通讯录退出的时候,把信息写到文件。

      当通讯录初始化后,加载文件的信息到通讯录中。


2.3.1初始化通讯录    


             通讯录是动态的,所以需要扩容,给一定空间。

void InitContact(Contact* pc)
{
  // 此时通讯录是空的,应先为通讯录分配空间
  pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * 4);
  // 如果分配成功,将通讯录的size设为0,capacity设为初识大小
  pc->size = 0;
  pc->capacity = 4;
  //将文档的信息传递到动态内存里,函数实现在下方
  LoadContact(pc);
}


2.3.2文件信息传递到通讯录里

将文件的信息加载到通讯录里

void LoadContact(Contact* pc)
{
  // 以读的形式打开文件
  FILE* pf = fopen("contact.txt", "rb");
  if (pf == NULL)
  {
    perror("fopen");
    return;
  }
  // 将文件中的内容加载到通讯录中
  // 这里用fread函数,当fread函数读取的联系人信息数为0时,说明读取结束
  PeoInfo tmp = { 0 }; // 定义一个联系人信息的结构体,便于读取
  int i = 0;
  while (fread(&tmp, sizeof(PeoInfo), 1, pf))
  {
    CheckCapacity(pc); // 在这个函数中查看数组是否需要扩容,若需要,则扩容
    pc->data[i] = tmp;
    pc->size++;
    i++;
  }
  // 关闭文件
  fclose(pf);
  pf = NULL;
  return;
}


2.3.3扩容通讯录

       添加联系人之前,需要判断通讯录是否满员,满员则需要扩容

void CheckCapacity(Contact* pc)
{
  // 判断通讯录是否已满,若满,进行扩容
  if (pc->size == pc->capacity)
  {
    PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 4);
    if (tmp == NULL)
    {
      printf("realloc fail\n");
      return;
    }
    pc->data = tmp;
    // 若扩容成功,增大capacity
    pc->capacity *= 2;
  }
}


2.3.4增加联系人

           需要添加联系人的各项信息。

void AddContact(Contact* pc)
{
  int num = 0;
  printf("添加人数:");
  scanf("%d", &num);
  // 输入要添加的联系人的信息
  // 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息
  for (int i = 0; i < num; i++)
  {
    //判断通讯录是否满人
    CheckCapacity(pc);
    printf("请输入名字\n");
    scanf("%s", pc->data[pc->size].name);
    printf("请输入性别\n");
    scanf("%s", pc->data[pc->size].sex);
    printf("请输入年龄\n");
    scanf("%d", &pc->data[pc->size].age);
    printf("请输入电话\n");
    scanf("%s", pc->data[pc->size].phone);
    pc->size++; // 将存入的联系人的数量加1
  }
}


2.3.5删除联系人

     需要找到该联系人的下标值,然后进行删除。

void DeleteContact(Contact* pc)
{
  int ret = 0;//记录寻找的下标值
  printf("请输入要删除的联系人的名字\n");
  char name[20];
  scanf("%s", name);
  // 定义一个新函数find,用来查找是否有这个联系人
  // 如果有,返回联系人的下标,如果没有,返回-1
  for (int i = 0; i < pc->size; i++)
  {
    if (strcmp(pc->data[i].name, name) == 0)
    {
      ret = i; break;
    }
  }
  if (ret)
  {
    printf("不存在");
  }
  else {
    for (int i = ret; i < pc->size - 1; i++)
    {
      pc->data[i] = pc->data[i + 1];
    }
    pc->size--;
  }
}


2.3.6查询联系人

找到下标值,从而得到联系人的信息

void SearchContact(Contact* pc)
{
  int ret = 0;
  printf("请输入要查找的联系人的名字\n");
  char name[20];
  scanf("%s", name);
  // 利用已经定义的find函数进行查找
  for (int i = 0; i < pc->size; i++)
  {
    if (strcmp(pc->data[i].name, name) == 0)
    {
      ret = i; break;
    }
  }
  if (ret)
  {
    printf("没有找到该联系人\n");
  }
  else
  {
    // 如果找到,打印该联系人的信息,首先打印五个标题
    printf("%-10s\t%-10s\t%-5s\t%-15s\n", "姓名", "性别", "年龄", "电话");
    printf("%-10s\t%-10s\t%-5d\t%-15s%\n",
      pc->data[ret].name,
      pc->data[ret].sex,
      pc->data[ret].age,
      pc->data[ret].phone
      );
  }
}


2.3.7修改联系人

找到下标值,从而进行各项数据的修改

void ModifyContact(Contact* pc)
{
  int ret = 0;
  printf("请输入要查找的联系人的名字\n");
  char name[20];
  scanf("%s", name);
  for (int i = 0; i < pc->size; i++)
  {
    if (strcmp(pc->data[i].name, name) == 0)
    {
      ret = i; break;
    }
  }
  if (ret)
  {
    printf("没有找到该联系人\n");
  }
  else
  {
    printf("请输入名字\n");
    scanf("%s", pc->data[ret].name);
    printf("请输入性别\n");
    scanf("%s", pc->data[ret].sex);
    printf("请输入年龄\n");
    scanf("%d", &pc->data[ret].age);
    printf("请输入电话\n");
    scanf("%s", pc->data[ret].phone);
  }
}


2.3.8打印通讯录

打印每个人的具体数据

// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{
  // 首先打印五个标题
  printf("%-10s\t%-10s\t%-5s\t%-15s\n", "姓名", "性别", "年龄", "电话");
  // 然后用for循环打印所有联系人的信息
  for (int i = 0; i < pc->size; i++)
  {
    printf("%-10s\t%-10s\t%-5d\t%-15s\n",
      pc->data[i].name,
      pc->data[i].sex,
      pc->data[i].age,
      pc->data[i].phone 
    );
  }
}


2.3.9信息保留在文件中

程序退出时,要将销毁空间,并且把数据存入文件里,方便下次调用

void SaveContact(Contact* pc)
{
  FILE* p = fopen("contact.txt", "wb");
  if (p == NULL)
  {
    perror("SaveContact");
  }
  else {
    int i = 0;
    for (i; i < pc->size; i++)
    {
      fwrite(pc->data + i, sizeof(PeoInfo), 1, p);
    }
    fclose(p);
    p = NULL;
    printf("保存成功");
  }
}


2.3.10销毁空间退出程序

void Exit(Contact* pc)
{
  //销毁空间
  free(pc->data);
  pc->data = NULL;
  pc->size = 0;
  pc->capacity = 0;
}


三.完整代码


和上面的代码比起来,连贯了Contact.c的各个函数实现。


3.1Text.c文件

#include"Contact.h"
void menu()
{
  printf("*********************************\n");
  printf("*******1.添加        2.删除*******\n");
  printf("*******3.查找        4.修改*******\n");
  printf("*******5.显示        6.保存*******\n");
  printf("*******0.退出              *******\n");
  printf("*********************************\n");
}
//使用函数指针数组
void (*(p[7]))(Contact*) = { Exit,AddContact,DeleteContact,SearchContact,ModifyContact,PrintContact,SaveContact };
int main()
{
  //定义结构体
  Contact cl;
  InitContact(&cl);
  int input;
  do {
    menu();
    printf("请输入->");
    scanf("%d", &input);
    if (input <= 6)
      p[input](&cl);
    else
      printf("输入错误\n");
  } while (input != 0);
}


3.2Contact.h文件

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<string.h>
typedef struct PeoInfo
{
  char name[10];//姓名
  int age;      //年龄
  char sex[3];  //性别
  int phone[12];//电话
}PeoInfo;
typedef struct Contact
{
  int size;  //当前存储的人数
  int capacity;  //通讯录容量大小
  PeoInfo* data;  //结构体指针,访问个人信息
}Contact;
//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//删除联系人
void DeleteContact(Contact* pc);
//查找联系人
void SearchContact(Contact* pc);
//修改联系人
void ModifyContact(Contact* pc);
//显示通讯录
void PrintContact(Contact* pc);
//扩容通讯录
void CheckCapacity(Contact* pc);
//销毁并且保存通讯录
void SaveContact(Contact* pc);
void LoadContact(Contact* pc);
void Exit(Contact* pc);

3.3Contact.c文件

#include"Contact.h"
void InitContact(Contact* pc)
{
  // 此时通讯录是空的,应先为通讯录分配空间
  pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * 4);
  // 如果分配成功,将通讯录的size设为0,capacity设为初识大小
  pc->size = 0;
  pc->capacity = 4;
  //将文档的信息传递到动态内存里,函数实现在下方
  LoadContact(pc);
}
void LoadContact(Contact* pc)
{
  // 以读的形式打开文件
  FILE* pf = fopen("contact.txt", "rb");
  if (pf == NULL)
  {
    perror("fopen");
    return;
  }
  // 将文件中的内容加载到通讯录中
  // 这里用fread函数,当fread函数读取的联系人信息数为0时,说明读取结束
  PeoInfo tmp = { 0 }; // 定义一个联系人信息的结构体,便于读取
  int i = 0;
  while (fread(&tmp, sizeof(PeoInfo), 1, pf))
  {
    CheckCapacity(pc); // 在这个函数中查看数组是否需要扩容,若需要,则扩容
    pc->data[i] = tmp;
    pc->size++;
    i++;
  }
  // 关闭文件
  fclose(pf);
  pf = NULL;
  return;
}
void CheckCapacity(Contact* pc)
{
  // 判断通讯录是否已满,若满,进行扩容
  if (pc->size == pc->capacity)
  {
    PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 4);
    if (tmp == NULL)
    {
      printf("realloc fail\n");
      return;
    }
    pc->data = tmp;
    // 若扩容成功,增大capacity
    pc->capacity *= 2;
  }
}
void AddContact(Contact* pc)
{
  int num = 0;
  printf("添加人数:");
  scanf("%d", &num);
  // 输入要添加的联系人的信息
  // 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息
  for (int i = 0; i < num; i++)
  {
    //判断通讯录是否满人
    CheckCapacity(pc);
    printf("请输入名字\n");
    scanf("%s", pc->data[pc->size].name);
    printf("请输入性别\n");
    scanf("%s", pc->data[pc->size].sex);
    printf("请输入年龄\n");
    scanf("%d", &pc->data[pc->size].age);
    printf("请输入电话\n");
    scanf("%s", pc->data[pc->size].phone);
    pc->size++; // 将存入的联系人的数量加1
  }
}
void DeleteContact(Contact* pc)
{
  int ret = 0;//记录寻找的下标值
  printf("请输入要删除的联系人的名字\n");
  char name[20];
  scanf("%s", name);
  // 定义一个新函数find,用来查找是否有这个联系人
  // 如果有,返回联系人的下标,如果没有,返回-1
  for (int i = 0; i < pc->size; i++)
  {
    if (strcmp(pc->data[i].name, name) == 0)
    {
      ret = i; break;
    }
  }
  if (ret)
  {
    printf("不存在");
  }
  else {
    for (int i = ret; i < pc->size - 1; i++)
    {
      pc->data[i] = pc->data[i + 1];
    }
    pc->size--;
  }
}
void SearchContact(Contact* pc)
{
  int ret = 0;
  printf("请输入要查找的联系人的名字\n");
  char name[20];
  scanf("%s", name);
  // 利用已经定义的find函数进行查找
  for (int i = 0; i < pc->size; i++)
  {
    if (strcmp(pc->data[i].name, name) == 0)
    {
      ret = i; break;
    }
  }
  if (ret)
  {
    printf("没有找到该联系人\n");
  }
  else
  {
    // 如果找到,打印该联系人的信息,首先打印五个标题
    printf("%-10s\t%-10s\t%-5s\t%-15s\n", "姓名", "性别", "年龄", "电话");
    printf("%-10s\t%-10s\t%-5d\t%-15s%\n",
      pc->data[ret].name,
      pc->data[ret].sex,
      pc->data[ret].age,
      pc->data[ret].phone
      );
  }
}
void ModifyContact(Contact* pc)
{
  int ret = 0;
  printf("请输入要查找的联系人的名字\n");
  char name[20];
  scanf("%s", name);
  // 利用已经定义的find函数进行查找
  for (int i = 0; i < pc->size; i++)
  {
    if (strcmp(pc->data[i].name, name) == 0)
    {
      ret = i; break;
    }
  }
  if (ret)
  {
    printf("没有找到该联系人\n");
  }
  else
  {
    printf("请输入名字\n");
    scanf("%s", pc->data[ret].name);
    printf("请输入性别\n");
    scanf("%s", pc->data[ret].sex);
    printf("请输入年龄\n");
    scanf("%d", &pc->data[ret].age);
    printf("请输入电话\n");
    scanf("%s", pc->data[ret].phone);
  }
}
// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{
  // 首先打印五个标题
  printf("%-10s\t%-10s\t%-5s\t%-15s\n", "姓名", "性别", "年龄", "电话");
  // 然后用for循环打印所有联系人的信息
  for (int i = 0; i < pc->size; i++)
  {
    printf("%-10s\t%-10s\t%-5d\t%-15s\n",
      pc->data[i].name,
      pc->data[i].sex,
      pc->data[i].age,
      pc->data[i].phone 
    );
  }
}
void SaveContact(Contact* pc)
{
  FILE* p = fopen("contact.txt", "wb");
  if (p == NULL)
  {
    perror("SaveContact");
  }
  else {
    int i = 0;
    for (i; i < pc->size; i++)
    {
      fwrite(pc->data + i, sizeof(PeoInfo), 1, p);
    }
    fclose(p);
    p = NULL;
    printf("保存成功");
  }
}
void Exit(Contact* pc)
{
  //销毁空间
  free(pc->data);
  pc->data = NULL;
  pc->size = 0;
  pc->capacity = 0;
}



总结


        在写动态存储文件版的通讯录,我们得学会动态存储和文件操作。


        只有不断的掌握知识,我们才能敲代码得心应手,在不断的学习里,逐渐进步。


目录
相关文章
|
18天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
54 3
|
2月前
|
存储 编译器 C语言
如何在 C 语言中判断文件缓冲区是否需要刷新?
在C语言中,可以通过检查文件流的内部状态或使用`fflush`函数尝试刷新缓冲区来判断文件缓冲区是否需要刷新。通常,当缓冲区满、遇到换行符或显式调用`fflush`时,缓冲区会自动刷新。
|
2月前
|
存储 编译器 C语言
C语言:文件缓冲区刷新方式有几种
C语言中文件缓冲区的刷新方式主要包括三种:自动刷新(如遇到换行符或缓冲区满)、显式调用 fflush() 函数强制刷新、以及关闭文件时自动刷新。这些方法确保数据及时写入文件。
|
2月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
32 2
|
2月前
|
存储 C语言
C语言中的浮点数存储:深入探讨
C语言中的浮点数存储:深入探讨
|
2月前
|
C语言
【C语言】探索文件读写函数的全貌(三)
【C语言】探索文件读写函数的全貌
|
2月前
|
存储 C语言
【C语言】探索文件读写函数的全貌(二)
【C语言】探索文件读写函数的全貌
|
2月前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
2月前
|
存储 C语言
手把手教你用C语言实现通讯录管理系统
手把手教你用C语言实现通讯录管理系统
|
2月前
|
C语言
【C语言】探索文件读写函数的全貌(一)
【C语言】探索文件读写函数的全貌