C语言小程序:通讯录(文件版)

简介: 基于通讯录的动态版本再次改造出来的关于文件版本的通讯录,适配的范围更广。

在静态版本的通讯录里面实现了通讯录的基本逻辑,但是空间大小是固定的,这样子就会显得很不灵活,会有浪费空间和空间不足的问题,所以在动态版本的通讯里面我们通过动态内存开辟使用malloc函数给通讯录动态的开辟一块空间,如果空间不够就使用realloc函数来扩容,这样子就实现了动态化的通讯录,解决了浪费空间和空间不足的问题,但是以上两个版本的通讯录在程序运行起来之后,将联系人的信息存起来之后,退出程序之后再次进入程序之前保存的联系人的信息就不见了,那么就可以在动态版本的基础上将其改变为文件版本,之前的通讯录的信息存放在一个文件中,下次进入程序的时候可以打开这个文件,获得联系人的信息,那么文件版本就实现了通讯录的动态大小以及联系人的永久储存。

通讯录(静态版)

通讯录(动态版)

紫蓝色几何渐变科技互联网微信公众号封面 (1).gif

目录

1.保存数据

2.读取文件

3.完整代码


1.保存数据

在第一次进入通讯录,添加联系人的信息之后,我们需要在退出通讯录的时候将联系人的信息保存在一个文件里面,以便下一次再进入通讯录的时候进行读取,所以应该在退出通讯录的前面再分装一个函数用来保存联系人的信息。

那这个保存练习人的函数该怎么实现呢?我们需要进行创建文件,然后以二进制的方式进行写文件,需要用到有关文件操作的函数

源文件:test.c

case EXIT:
      //保存信息
      SaveContact(&con);
      //释放通讯录的空间
      DestroyContact(&con);
      printf("退出通讯录!\n");
      break;

image.gif

头文件:Contact.h

//保存联系人的信息到文件中
void SaveContact(Contact* pc);

image.gif

源文件:Contact.c

//保存联系人的信息
void SaveContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "wb");
  if (pf == NULL)
  {
    perror("SaveContact::fopen");
    return;
  }
  //保存信息
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf);
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
  printf("保存成功...\n");
}

image.gif

如果程序运行起来之后,添加了几个联系人然后退出程序,这时联系人的信息已经保存到了contact.txt这个文本文件中,如果我们找到这个文件打开它发现里面的东西大部分都不认识,这是因为我们在保存的时候是以二进制的方式进行保存,但是这个文件是一个文本文件,所以就和我们看到的不一样:

image.gif编辑

2.读取文件

保存好了文件之后,在下一次进入通讯录的时候就需要打开这个文件,先读取以前保存的联系人的信息,所以就需要在初始化通讯录的部分再分装一个函数来进行读取通讯录

那要怎么读取呢?打开文件的时候需要以二进制读的方式打开,然后读取文件的时候先要判断联系人的个数,然后在此基础上判断是否需要进行对通讯录的扩容,然后再进行读取联系人的数据

源文件:Contact.c

//读取文件
int CheckCapacity(Contact* pc); //对扩容函数进行声明,以方便下面的使用
void LoadContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "rb");
  if (pf == NULL)
  {
    perror("LoadContact::fopen");
    return;
  }
  //读取联系人的信息
  //fread函数的返回值是size_t的整形,所以当读不到数据的时候返回0
  //因此就可以使用fread函数的返回值作为循环条件来读取数据
  //
  //创建一个变量将读取到的联系人的信息放在里面,然后再将它放进通讯录里面
  PeoInfo tmp = { 0 };
  while (fread(&tmp, sizeof(PeoInfo), 1, pf))
  {
    //因为不知道能读取多少个联系人的信息,所以先要进行容量的判断
    CheckCapacity(pc);
    //将读取到的联系人的信息传到通讯录里面
    pc->data[pc->sz] = tmp;
    pc->sz++;
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
}
//初始化通讯录
void InitContact(Contact* pc)
{
  pc->data = (PeoInfo*)malloc(DEFUAULT_SZ * sizeof(PeoInfo));
  if (pc->data == NULL)
  {
    printf("通讯录初始化失败:%s\n", strerror(errno));
    return;
  }
  pc->sz = 0;
  pc->capacity = DEFUAULT_SZ;
  //读取联系人的信息
  LoadContact(pc);
}
int CheckCapacity(Contact* pc)
{
  //如果满容量了,那就需要扩充PeoInfo的容量了
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
    if (ptr == NULL)
    {
      printf("CheckCapacity:%s\n", strerror(errno));
      return 0;
    }
    else
    {
      pc->data = ptr;  //将扩容之后的地址传给data
      pc->capacity += INC_SZ;  //给容量加2
      printf("增容成功,当前容量:%d\n", pc->capacity);
      return 1;
    }
  }
  return 1;
}

image.gif

头文件:Contact.h

//读取联系人信息
void LoadContact(Contact* pc);

image.gif

3.完整代码

源文件:test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
//测试通讯录相关的功能
void menu()
{
  printf("**************************************\n");
  printf("****  1.Add     *****    2.Del    ****\n");
  printf("****  3.Search  *****    4.Modify ****\n");
  printf("****  5.Show    *****    6.Sqrt   ****\n");
  printf("****  7.Clear   *****    0.Exit   ****\n");
  printf("**************************************\n");
}
enum Option
{
  EXIT,
  ADD,
  DEL,
  SEARCH,
  MODIFY,
  SHOW,
  SQRT,
  CLEAR
};
int main()
{
  int input = 0;
  Contact con;   //创建通讯录变量
  //初始化通讯录
  InitContact(&con);  //要修改通讯录里面的内容要传递指针
  do
  {
    menu();
    printf("请选择通讯录的功能:>");
    scanf("%d", &input);
    switch (input)
    {
    case ADD:
      //添加联系人
      printf("添加联系人\n");
      AddContact(&con); //传址调用
      break;
    case DEL:
      //删除联系人
      printf("删除联系人\n");
      DelContact(&con);
      break;
    case SEARCH:
      //查找联系人
      printf("查找联系人\n");
      SearchContact(&con);
      break;
    case MODIFY:
      //修改联系人
      printf("修改联系人\n");
      ModifyContact(&con);
      break;
    case SHOW:
      //展示
      ShowContact(&con);
      //显示通讯录不需要修改通讯录其实传值调用也可以,但是为了节省空间使用传值调用
      break;
    case SQRT:
      //排序联系人
      SqrtContact(&con);
      break;
    case CLEAR:
      //清空联系人a
      ClearContact(&con);
      break;
    case EXIT:
      //保存信息
      SaveContact(&con);
      //释放通讯录的空间
      DestroyContact(&con);
      printf("退出通讯录!\n");
      break;
    default:
      printf("选择错误,请重新选择:>\n");
      break;
    }
  } while (input);
  return 0;
}

image.gif

头文件:Contact.h

#pragma once
//头文件的包含
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
//大小的定义
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 20
#define DEFUAULT_SZ 5
#define INC_SZ 3
//表示一个联系人的各种信息
typedef struct PeoInfo
{
  char name[MAX_NAME];
  int age;
  char sex[MAX_SEX];
  char tele[MAX_TELE];
  char addr[MAX_ADDR];
}PeoInfo;
//通讯录
typedef struct Contact
{
  PeoInfo* data;   //用来存放联系人的信息
  int sz;          //记录通讯录中有效信息的个数
  int capacity;    //当前通讯录的一个容量
}Contact;
//函数的声明
//初始化通讯录
void InitContact(Contact* pc);
//添加联系人
void AddContact(Contact* pc);
//显示通讯录
void ShowContact(const Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(const Contact* pc);
//修改联系人
void ModifyContact(Contact* pc);
//排序联系人
void SqrtContact(Contact* pc);
//清空联系人
void ClearContact(Contact* pc);
//释放内存
void DestroyContact(Contact* pc);
//保存联系人的信息到文件中
void SaveContact(Contact* pc);
//读取联系人信息
void LoadContact(Contact* pc);

image.gif

源文件:Contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
//通讯录的实现模块
//读取文件
int CheckCapacity(Contact* pc); //对扩容函数进行声明,以方便下面的使用
void LoadContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "rb");
  if (pf == NULL)
  {
    perror("LoadContact::fopen");
    return;
  }
  //读取联系人的信息
  //fread函数的返回值是size_t的整形,所以当读不到数据的时候返回0
  //因此就可以使用fread函数的返回值作为循环条件来读取数据
  //
  //创建一个变量将读取到的联系人的信息放在里面,然后再将它放进通讯录里面
  PeoInfo tmp = { 0 };
  while (fread(&tmp, sizeof(PeoInfo), 1, pf))
  {
    //因为不知道能读取多少个联系人的信息,所以先要进行容量的判断
    CheckCapacity(pc);
    //将读取到的联系人的信息传到通讯录里面
    pc->data[pc->sz] = tmp;
    pc->sz++;
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
}
//初始化通讯录
void InitContact(Contact* pc)
{
  pc->data = (PeoInfo*)malloc(DEFUAULT_SZ * sizeof(PeoInfo));
  if (pc->data == NULL)
  {
    printf("通讯录初始化失败:%s\n", strerror(errno));
    return;
  }
  pc->sz = 0;
  pc->capacity = DEFUAULT_SZ;
  //读取联系人的信息
  LoadContact(pc);
}
int CheckCapacity(Contact* pc)
{
  //如果满容量了,那就需要扩充PeoInfo的容量了
  if (pc->sz == pc->capacity)
  {
    PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
    if (ptr == NULL)
    {
      printf("CheckCapacity:%s\n", strerror(errno));
      return 0;
    }
    else
    {
      pc->data = ptr;  //将扩容之后的地址传给data
      pc->capacity += INC_SZ;  //给容量加2
      printf("增容成功,当前容量:%d\n", pc->capacity);
      return 1;
    }
  }
  return 1;
}
//添加联系人
void AddContact(Contact* pc)
{
  //判断通讯录是否满容量
  if (0 == CheckCapacity(pc))
  {
    printf("空间不够,扩容失败\n");
    return;
  }
  else
  {
    printf("请输入名字:>");
    scanf("%s", pc->data[pc->sz].name);
    printf("请输入年龄:>");
    scanf("%d", &(pc->data[pc->sz].age));
    printf("请输入性别:>");
    scanf("%s", pc->data[pc->sz].sex);
    printf("请输入电话:>");
    scanf("%s", pc->data[pc->sz].tele);
    printf("请输入地址:>");
    scanf("%s", pc->data[pc->sz].addr);
    pc->sz++;   //每一次添加完之后sz都要++
    printf("添加成功\n");
  }
}
//显示通讯录
void ShowContact(const Contact* pc)
{
  printf("%-10s %-4s %-5s %-12s %-15s\n", "名字", "年龄", "性别", "电话", "地址");
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    printf("%-10s %-4d %-5s %-12s %-15s\n",
      pc->data[i].name,
      pc->data[i].age,
      pc->data[i].sex,
      pc->data[i].tele,
      pc->data[i].addr);
  }
}
//查找
static int FindByName(const Contact* pc, char name[])
{
  int i = 0;
  int pos = 0;
  for (i = 0; i < pc->sz; i++)
  {
    //找到所要联系人所在的位置
    if (0 == strcmp(pc->data[i].name, name))
    {
      return i;
    }
  }
  return -1;
}
//删除联系人
void DelContact(Contact* pc)
{
  char name[MAX_NAME] = { 0 };
  //判断通讯录是否为空
  if (0 == pc->sz)
  {
    printf("通讯录为空,无法删除!\n");
    return;
  }
  printf("请输入你要删除联系人的姓名:>");
  scanf("%s", name);
  int pos = FindByName(pc, name);
  int i = 0;
  if (pos == -1)
  {
    printf("找不到所要删除的联系人\n");
    return;
  }
  //进行删除
  for (i = pos; i < pc->sz - 1; i++) //判断条件这里所要交换的比总数少一个
  {
    pc->data[i] = pc->data[i + 1];
  }
  pc->sz--;
  printf("删除成功\n");
}
//查找联系人
void SearchContact(const Contact* pc)
{
  char name[MAX_NAME] = { 0 };
  if (pc->sz == 0)
  {
    printf("通讯录为空,查找不到!");
    return;
  }
  printf("请输入你要查找联系人的姓名:>");
  scanf("%s", name);
  int pos = FindByName(pc, name);
  int i = 0;
  if (pos == -1)
  {
    printf("找不到所要查找的联系人\n");
    return;
  }
  //打印导航栏
  printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");
  //打印数据
  printf("%-10s %-4d %-5s %-12s %-30s\n",
    pc->data[pos].name,
    pc->data[pos].age,
    pc->data[pos].sex,
    pc->data[pos].tele,
    pc->data[pos].addr);
}
//修改联系人
void menu1()
{
  printf("*********************************\n");
  printf("******1.姓名*********2.年龄*******\n");
  printf("******3.性别*********4.电话*******\n");
  printf("*************5.地址**************\n");
  printf("*********************************\n");
}
void ModifyContact(Contact* pc)
{
  int input = 0;
  char name[MAX_NAME] = { 0 };
  printf("请输入你要修改的联系人姓名:>");
  scanf("%s", name);
  //查找
  int pos = FindByName(pc, name);
  int i = 0;
  if (pos == -1)
  {
    printf("找不到所要查找的联系人\n");
    return;
  }
  //修改
  printf("请输入要修改的具体信息:>\n");
  menu1();
  scanf("%d", &input);
  switch (input)
  {
  case 1:
    printf("请输入新的姓名:>");
    scanf("%s", pc->data[pos].name);
    break;
  case 2:
    printf("请输入新的年龄:>");
    scanf("%d", &pc->data[pos].age);
    break;
  case 3:
    printf("请输入新的性别:>");
    scanf("%s", pc->data[pos].sex);
    break;
  case 4:
    printf("请输入新的电话:>");
    scanf("%s", pc->data[pos].tele);
    break;
  case 5:
    printf("请输入新的地址:>");
    scanf("%s", pc->data[pos].addr);
    break;
  default:
    printf("输入有误,修改失败\n");
    return;
  }
  printf("修改成功\n");
}
void menu2()
{
  printf("***************************\n");
  printf("******** 1.NAME  **********\n");
  printf("******** 2.AGE   **********\n");
  printf("***************************\n");
}
//按照名字排序
int cmp_byname(const void* p1, const void* p2)
{
  return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name);
}
//按照年龄来排序
int cmp_byage(const void* p1, const void* p2)
{
  return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age;
}
//排序联系人
void SqrtContact(Contact* pc)
{
  int input = 0;
  if (pc->sz == 0)
  {
    printf("通讯录没有联系人,无法排序!\n");
    return;
  }
  menu2();
  printf("请选择排序的对象:>");
  scanf("%d", &input);
  switch (input)
  {
  case 1:
    qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byname);
    break;
  case 2:
    qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byage);
    break;
  }
  printf("排序成功\n");
  //排序成功之后打印一下
  ShowContact(pc);
}
//清空联系人
void ClearContact(Contact* pc)
{
  //初始化通讯录
  InitContact(pc);
  printf("清空成功\n");
  //初始完之后再打印
  ShowContact(pc);
}
//释放通讯录
void DestroyContact(Contact* pc)
{
  free(pc->data);
  pc->data = NULL;
  pc->sz = 0;
  pc->capacity = 0;
  printf("释放内存....\n");
}
//保存联系人的信息
void SaveContact(Contact* pc)
{
  //打开文件
  FILE* pf = fopen("contact.txt", "wb");
  if (pf == NULL)
  {
    perror("SaveContact::fopen");
    return;
  }
  //保存信息
  int i = 0;
  for (i = 0; i < pc->sz; i++)
  {
    fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf);
  }
  //关闭文件
  fclose(pf);
  pf = NULL;
  printf("保存成功...\n");
}

image.gif

关于文件版本的通讯录就分享到这里,感谢大家的阅览!

目录
相关文章
|
23天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
74 3
|
2月前
|
存储 编译器 C语言
如何在 C 语言中判断文件缓冲区是否需要刷新?
在C语言中,可以通过检查文件流的内部状态或使用`fflush`函数尝试刷新缓冲区来判断文件缓冲区是否需要刷新。通常,当缓冲区满、遇到换行符或显式调用`fflush`时,缓冲区会自动刷新。
|
2月前
|
存储 编译器 C语言
C语言:文件缓冲区刷新方式有几种
C语言中文件缓冲区的刷新方式主要包括三种:自动刷新(如遇到换行符或缓冲区满)、显式调用 fflush() 函数强制刷新、以及关闭文件时自动刷新。这些方法确保数据及时写入文件。
|
2月前
|
存储 C语言
探索C语言数据结构:利用顺序表完成通讯录的实现
本文介绍了如何使用C语言中的顺序表数据结构实现一个简单的通讯录,包括初始化、添加、删除、查找和保存联系人信息的操作,以及自定义结构体用于存储联系人详细信息。
35 2
|
2月前
|
C语言
【C语言】探索文件读写函数的全貌(三)
【C语言】探索文件读写函数的全貌
|
2月前
|
存储 C语言
【C语言】探索文件读写函数的全貌(二)
【C语言】探索文件读写函数的全貌
|
2月前
|
存储 C语言
手把手教你用C语言实现通讯录管理系统
手把手教你用C语言实现通讯录管理系统
|
2月前
|
C语言
【C语言】探索文件读写函数的全貌(一)
【C语言】探索文件读写函数的全貌
|
2月前
|
存储 文件存储 C语言
【C语言】深入了解文件:简明指南
【C语言】深入了解文件:简明指南
|
3月前
|
Linux C语言
C语言 文件IO (系统调用)
本文介绍了Linux系统调用中的文件I/O操作,包括文件描述符、`open`、`read`、`write`、`lseek`、`close`、`dup`、`dup2`等函数,以及如何获取文件属性信息(`stat`)、用户信息(`getpwuid`)和组信息(`getgrgid`)。此外还介绍了目录操作函数如`opendir`、`readdir`、`rewinddir`和`closedir`,并提供了相关示例代码。系统调用直接与内核交互,没有缓冲机制,效率相对较低,但实时性更高。
下一篇
DataWorks