应用顺序表——写一个通讯录

一、写在开始

顺序表写到这里,难免会好奇,这个顺序表有啥用?

这里,我们可以用顺序表写一个通讯录。

基础要求:我将要用到结构体、动态内存管理、顺序表、⽂件操作来实现通讯录的创造

功能:

我给他赋予了以下几个功能,并会教给大家怎样实现代码操作。

1)⾄少能够存储100个⼈的通讯信息
2)能够保存⽤⼾信息:名字、性别、年龄、电话、地址等
3)增加联系⼈信息
4)删除指定联系⼈
5)查找制定联系⼈
6)修改指定联系⼈
7)显⽰联系⼈信息

二、代码实现

1.原理

首先,我画一张图便于大家理解顺序表和通讯录有什么联系。

由图可见,一个顺序表是一个数组,一个数组对应的一个下标里面是一个结构体,每个结构体代表了一个通讯录里面一个人的信息,把他放在顺序表里面就是一个通讯录了。上一次我已经发过了一篇这样写顺序表的文章,这次的通讯录也离不开顺序表。

2.处理文件

  • 我们需要contact.h记录通讯录的结构以及函数的定义声明
  • 我们需要contact.c实现通讯录的函数
  • 我们需要seqlist.h记录顺序表结构,定义声明顺序表函数
  • 我们需要seqlist.c实现顺序表的函数
  • 我们需要test.c测试代码

3.定义联系人数据结构

既然是有关于联系人的定义,即写在contct.h中。

#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100

//定义通讯录的联系人姓名,性别,年龄,电话,地址
typedef struct personInfo {
	char name[NAME_MAX];
	char gender[GENDER_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}perinfo;

4.定义类型数据

此定义数据类型,是需要用到顺序表的,即是将此定义在seqlist.h中。

typedef int sltype;//方便后续替换数组类型

在写顺序表的时候我为了方便替换数据类型写了一条这样的代码,为了方便测试数据,我写了一个int型数组。而现在,我们知道了通讯录在数组里面是一个结构体,于是我们就可以直接将int型替换成数组类型。

typedef struct personInfo sltype;

这里如果想要将struct personInfo写成perinfo则需要在seqlist.h里面包含一下contact.h文件。

5.给顺序表改名字

既然要写通讯录,就给顺序表改个名字吧。

//contact.h
typedef struct seqlist contact;

6.通讯录函数

文件录入

写一个代码录入历史联系人数据,这里会用到文件操作。

void LoadContact(Contact* con) {
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL) {
		perror("fopen error!\n");
		return;
	}
	//循环读取⽂件数据
	perinfo info;
	while (fread(&info, sizeof(perinfo), 1, pf))
	{
		slpushback(con, info);
	}
	printf("历史数据已录入成功!\n");
}

要写函数先在contact.h里面把所有要写的函数都定义好

//通讯录的初始化
void ContactInit(Contact* con);
//通讯录的销毁
void ContactDesTroy(Contact* con);
//通讯录添加数据
void ContactAdd(Contact* con);
//通讯录删除数据
void ContactDel(Contact* con);
//通讯录的修改
void ContactModify(Contact* con);
//通讯录查找
void ContactFind(Contact* con);
//展示通讯录数据
void ContactShow(Contact* con);

函数都写在了contact.c文件里。

1.初始化通讯录

//contact.c
void ContactInit(contact* con) {
	//实际上就是进行顺序表初始化
	slinit(con);
    LoadContact(con);
}

2.添加通讯录数据

//通讯录添加数据
void ContactAdd(Contact* con)
{
	//获取用户输入的内容:姓名+性别+年龄+电话+地址
	perinfo info;
	printf("请输入要添加的联系人姓名:\n");
	scanf("%s", info.name);

	printf("请输入要添加的联系人性别:\n");
	scanf("%s", info.gender);

	printf("请输入要添加的联系人年龄:\n");
	scanf("%d", &info.age);

	printf("请输入要添加的联系人电话:\n");
	scanf("%s", info.tel);

	printf("请输入要添加的联系人住址:\n");
	scanf("%s", info.addr);

	//往通讯录中添加联系人数据
	slpushback(con, info);
}

3.删除通讯录数据

先写一个姓名查找。

//姓名查找联系人
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;
}
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");
}

4.展示通讯录数据

void ContactShow(Contact* con)
{
	//表头:姓名  性别 年龄 电话  地址
	printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
	//遍历通讯录,按照格式打印每个联系人数据
	for (int i = 0; i < con->size; i++)
	{
		printf("%3s %3s %3d %3s %3s\n", //手动调整一下格式
			con->arr[i].name,
			con->arr[i].gender,
			con->arr[i].age,
			con->arr[i].tel,
			con->arr[i].addr
		);
	}
}

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;
}

6.修改通讯录数据

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");
	scanf("%s", con->arr[find].gender);

	printf("请输入新的年龄:\n");
	scanf("%d", &con->arr[find].age);

	printf("请输入新的电话:\n");
	scanf("%s", con->arr[find].tel);

	printf("请输入新的住址:\n");
	scanf("%s", con->arr[find].addr);

	printf("修改成功!\n");
}

写完保存一下通讯录的数据,这里又要用到文件操作。

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(perinfo), 1, pf);
	}
	printf("通讯录数据保存成功!\n");
}

7.销毁通讯录数据

void ContactDesTroy(Contact* con)
{
    SaveContact(con);
	sldestroy(con);
}

三、完整demo

//seqlist.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"contact.h"
typedef perinfo sltype;
typedef struct seqlist
{
	sltype* arr;
	int size;
	int capacity;
}sl;
//typedef struct seqlist sl;
//初始化和销毁
void slinit(sl* ps);
void sldestroy(sl* ps);
//扩容
void slCheckCapacity(sl* ps);
//尾部插入删除/头部插入删除 
void slpushback(sl* ps, sltype x);
void slpopback(sl* ps);
void slpushfront(sl* ps, sltype x);
void slpopfront(sl* ps);
//指定位置之前插入/删除数据
void slinsert(sl* ps, int pos, sltype x);
void slerase(sl* ps, int pos);
//seqlist.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
void slinit(sl* ps) {
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}
//申请空间
void slCheckCapacity(sl* ps) {
	if (ps->capacity == ps->size) {
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		sltype* tmp=(sltype*)realloc(ps->arr, newcapacity * sizeof(sltype));
		if (tmp == NULL) {
			perror("realloc");
			exit(1);//直接退出,程序不再执行
		}
		ps->arr = tmp;
		ps->capacity = newcapacity; 
	}
}
void sldestroy(sl* ps) {
	if (ps->arr) {
		free(ps->arr);
	}
	ps->arr = NULL;
	ps->size = ps->capacity = 0;

}
//尾部插入删除
void slpushback(sl* ps, sltype x) {
	assert(ps);
	slCheckCapacity(ps);
	ps->arr[ps->size++] = x;
}
void slpopback(sl* ps) {
	assert(ps);
	//顺序表不能为空
	assert(ps->size);
	//只进行--操作不影响增删查改
	--ps->size;
}
//头部插入删除
void slpushfront(sl* ps, sltype x) {
	assert(ps);
	slCheckCapacity(ps);
	for (int i = ps->size; i > 0; i--) {
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}
void slpopfront(sl* ps) {
	assert(ps);
	assert(ps->size);
	int i;
	for (i = 0; i < ps->size; i++) {
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}
//指定位置之前插入
void slinsert(sl* ps, int pos, sltype x) {
	assert(ps);
	assert(pos >= 0&&pos<=ps->size);
	//插入数据,空间够不够
	slCheckCapacity(ps);
	//让pos及之后的数据整体往后挪
	int i;
	for (i = ps->size; i > pos; i--) {
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}
//删除指定位置的数据
void slerase(sl* ps, int pos) {
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int i;
	for (i = pos; i < ps->size-1; i++) {
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}
//contact.h
#pragma once
#define NAME_MAX 20
#define GENDER_MAX 10 
#define TEL_MAX 20
#define ADDR_MAX 100
//定义联系人数据 结构
//姓名 性别 年龄 电话 地址
typedef struct personInfo
{
	char name[NAME_MAX];
	char gender[GENDER_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}perinfo;

//给顺序表改个名字,叫做通讯录
typedef struct seqlist Contact; //sl

//通讯录的初始化
void ContactInit(Contact* con);
//通讯录的销毁
void ContactDesTroy(Contact* con);
//通讯录添加数据
void ContactAdd(Contact* con);
//通讯录删除数据
void ContactDel(Contact* con);
//通讯录的修改
void ContactModify(Contact* con);
//通讯录查找
void ContactFind(Contact* con);
//展示通讯录数据
void ContactShow(Contact* con);
//contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
#include"seqList.h"

void LoadContact(Contact* con) {
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL) {
		perror("fopen error!\n");
		return;
	}
	//循环读取⽂件数据
	perinfo info;
	while (fread(&info, sizeof(perinfo), 1, pf))
	{
		slpushback(con, info);
	}
	printf("历史数据已录入成功!\n");
}
//通讯录的初始化
void ContactInit(Contact* con)//sl
{
	//实际上要进行的是顺序表的初始化
	//顺序表的初始化已经实现好了
	slinit(con);
	LoadContact(con);
}

//通讯录添加数据
void ContactAdd(Contact* con)
{
	//获取用户输入的内容:姓名+性别+年龄+电话+地址
	perinfo info;
	printf("请输入要添加的联系人姓名:\n");
	scanf("%s", info.name);

	printf("请输入要添加的联系人性别:\n");
	scanf("%s", info.gender);

	printf("请输入要添加的联系人年龄:\n");
	scanf("%d", &info.age);

	printf("请输入要添加的联系人电话:\n");
	scanf("%s", info.tel);

	printf("请输入要添加的联系人住址:\n");
	scanf("%s", info.addr);

	//往通讯录中添加联系人数据
	slpushback(con, info);
}

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;
}

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");
}

//展示通讯录数据
void ContactShow(Contact* con)
{
	//表头:姓名  性别 年龄 电话  地址
	printf("%s %s %s  %s     %s\n", "姓名", "性别", "年龄", "电话", "地址");
	//遍历通讯录,按照格式打印每个联系人数据
	for (int i = 0; i < con->size; i++)
	{
		printf("%s %3s %4d %-8s %6s\n", //手动调整一下格式
			con->arr[i].name,
			con->arr[i].gender,
			con->arr[i].age,
			con->arr[i].tel,
			con->arr[i].addr
		);
	}
}
//通讯录的修改
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");
	scanf("%s", con->arr[find].gender);

	printf("请输入新的年龄:\n");
	scanf("%d", &con->arr[find].age);

	printf("请输入新的电话:\n");
	scanf("%s", con->arr[find].tel);

	printf("请输入新的住址:\n");
	scanf("%s", con->arr[find].addr);

	printf("修改成功!\n");
}
//通讯录查找
void ContactFind(Contact* con)
{
	//11

	char name[NAME_MAX];
	printf("请输入要查找的联系人姓名\n");
	scanf("%s", name);

	int find = FindByName(con, name);
	if (find < 0)
	{
		printf("要查找的联系人数据不存在!\n");
		return;
	}
	// 姓名 性别 年龄 电话  地址
	// 11   11   11   11   11
	printf("%s %s %s  %s     %s\n", "姓名", "性别", "年龄", "电话", "地址");
	printf("%s %3s %4d %-8s %6s\n", //手动调整一下格式
		con->arr[find].name,
		con->arr[find].gender,
		con->arr[find].age,
		con->arr[find].tel,
		con->arr[find].addr
	);
}

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(perinfo), 1, pf);
	}
	printf("通讯录数据保存成功!\n");
}

//通讯录的销毁
void ContactDesTroy(Contact* con)
{
	SaveContact(con);
	sldestroy(con);
}

我们可以在测试代码里面写一个菜单,使得通讯录更加完整。

//text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
void menu()
{
	printf("******************通讯录******************\n");
	printf("*******1.增加联系人   2.删除联系人********\n");
	printf("*******3.修改联系人   4.查找联系人********\n");
	printf("*******5.展示联系人   0.   退出  *********\n");
	printf("******************************************\n");
}

int main()
{
	int op = -1;
	Contact con;
	ContactInit(&con);

	do {
		menu();
		printf("请选择您的操作:\n");
		scanf("%d", &op);

		//要根据对应的op执行不同的操作
		switch (op)
		{
		case 1:
			ContactAdd(&con);
			break;
		case 2:
			ContactDel(&con);
			break;
		case 3:
			ContactModify(&con);
			break;
		case 4:
			ContactFind(&con);
			break;
		case 5:
			ContactShow(&con);
			break;
		case 0:
			printf("退出通讯录....\n");
			break;
		default:
			printf("输入错误,请重新选择您的操作!\n");
			break;
		}
	} while (op != 0);

	ContactDesTroy(&con);
	return 0;
}

四、小结

我写这篇文章,是为了让大家在学习了顺序表后并能运用它,顺序表作为数据结构的底层,是非常有必要学习的理解的,希望这篇文章对大家有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值