【C++】C++应用案例-通讯录管理系统

目录

一、整体介绍

1.1、需求和目标

1.2、整体功能描述

二、页面及功能描述

[2.1 主菜单](#2.1 主菜单)

[2.2 添加联系人菜单](#2.2 添加联系人菜单)

[2.3 显示联系人菜单](#2.3 显示联系人菜单)

[2.4 修改联系人菜单](#2.4 修改联系人菜单)

[2.5 退出功能](#2.5 退出功能)

三、流程设计

[3.1 主流程](#3.1 主流程)

[3.2 添加操作流程](#3.2 添加操作流程)

[3.3 显示联系人操作流程](#3.3 显示联系人操作流程)

[3.4 修改联系人操作流程](#3.4 修改联系人操作流程)

四、代码设计

[4.1 核心思路](#4.1 核心思路)

[4.2 项目文件分类设计](#4.2 项目文件分类设计)

[4.2.1 头文件](#4.2.1 头文件)

[4.2.2 源文件](#4.2.2 源文件)

[4.2.3 代码实现步骤](#4.2.3 代码实现步骤)

[4.3 代码具体实现](#4.3 代码具体实现)

[4.3.1 定义头文件](#4.3.1 定义头文件)

[4.3.2 实现主体流程](#4.3.2 实现主体流程)

[4.3.3 绘制菜单](#4.3.3 绘制菜单)

[4.3.4 读取键盘输入](#4.3.4 读取键盘输入)

[4.3.5 实现具体操作功能](#4.3.5 实现具体操作功能)


一、整体介绍

1.1、需求和目标

项目的需求,实现一个叫做"通讯录管理"的基于命令行界面的通讯录管理软件。

我们的目标,是通过项目练习,初步掌握编写软件的方法和技巧。

1.2、整体功能描述

这个软件相对简单,只需要基于命令行做纯文本的交互,不涉及图形化界面。作为一个记录通讯录软件,主要有三大功能:添加联系人、显示联系人与修改联系人。也就是说,软件需要能够记录联系人的姓名与联系方式等相关信息,并提供查询显示联系人信息的功能,也可对联系人信息进行修改。而且为了让软件有真正的实用价值,需要对数据做持久保存:我们这里只需要存放在一个指定的文本文件中。

项目采用分级菜单方式,每一级菜单应该有"返回主菜单"功能;主菜单有"退出"选项。

二、页面及功能描述

2.1 主菜单

运行软件后首先应该显示主菜单。主菜单提供四个选项:"添加联系人"、"显示联系人"、"修改联系人"和"退出",并在下方提示用户输入1-4的数字,选择不同的功能。

主菜单界面如下:

2.2 添加联系人菜单

在主菜单页面选择"1",可以进入记账菜单(二级菜单)。添加联系人菜单提供三个选项:"办公"、"个人"和"退出";并在下方提示用户按对应的数字进行功能选择。

记账菜单界面如下:

在添加联系人菜单页面选择"1",可以记录一条办公类联系人信息。联系人信息包括:类型(办公/个人)、姓名、电话、住址。这里需要提示用户输入联系人的姓名、电话与住址信息,然后显示完成添加。

类似地,在联系人菜单页面选择"2",可以记录一条个人类联系人信息。提示用户输入联系人的姓名、电话与住址,然后显示完成添加。

2.3 显示联系人菜单

在主菜单页面选择"2",可以进入显示联系人菜单。显示联系人菜单提供四个选项:"全部"、"办公"、"个人"和"退出";并在下方提示用户按对应的数字进行功能选择。

显示联系人菜单界面如下:

在显示联系人菜单页面,选择"1"可以显示全部联系人信息;选择"2"可以显示所有办公类联系人;选择"3"可以显示所有个人类联系人。用户选择之后,除了应该显示对应的联系人信息外,还应该对所有联系人个数进行统计汇总。

对应地,选择"1"之后列出所有联系人,统计的是通讯录的总条数;选择"2"之后列出所有办公类联系人,统计办公类总条数;选择"3"之后列出所有个人类联系人,统计个人类总条数。

2.4 修改联系人菜单

在主菜单页面选择"3",可以进入修改联系人菜单。修改联系人菜单提供三个选项:"办公"、"个人"和"退出";并在下方提示用户按对应的数字进行功能选择。

显示联系人菜单界面如下:

在修改联系人菜单页面,选择"1"可以修改办公类别联系人信息;选择"2"可以个人类别联系人信息。用户选择之后,根据提示输入要修改的联系人姓名,如没有找到,提示未找到此人;如找到此联系人,输入更改后的姓名、电话与住址,并提示修改成功。

2.5 退出功能

在主菜单页面,选择"3"可以退出软件。页面将做确认退出的对话提醒:如果用户输入"Y"则退出,输入"N"则返回主菜单页面。

三、流程设计

3.1 主流程

3.2 添加操作流程

3.3 显示联系人操作流程

3.4 修改联系人操作流程

四、代码设计

4.1 核心思路

通讯录软件处理的数据,就是添加或者修改的"联系人"。每一条联系人数据,都应该包含类型、姓名、电话、住址四部分,这可以构建一个结构体类型 Person来表示。

而程序运行的开始,应该读取保存在文件中的数据,读取之后应该是一个 Person类型的数组。

流程控制方面 ,如果用户不选择退出,程序就不会结束,所以应该用一个while循环来处理整个流程;当用户确认退出时,更改一个标志位,用来退出循环。

每一级菜单 做键盘选择后,可以用switch 分支语句来处理;不同的功能模块,可以包装成函数。

4.2 项目文件分类设计

4.2.1 头文件

对于一个C++项目来说,构建整体架构很重要的一步就是定义头文件。一般我们会把全局变量、函数声明、以及结构体的定义都放在头文件中。

本项目可以设计两个头文件,来管理不同的内容:

  1. 结构体 Person的定义,以及涉及到账目操作的函数声明,都可以放在一个头文件 person_item.h 中;
  2. 其它一些通用的设置和功能性函数,可以放在另一个头文件common.h 中

4.2.2 源文件

真正的代码实现都放在源文件中。根据不同的用途,项目中可以用四个源文件来实现对应的功能

  1. 主体代码可以放在一个源文件 person_main.cpp 中
  2. 绘制菜单的函数都放在menus.cpp中
  3. 通用函数(比如读取键盘输入)放在read_input.cpp中
  4. 针对账目的所有操作函数(比如添加、显示与修改)全部放在operations.cpp中

4.2.3 代码实现步骤

1、实现整体框架和主题流程

  • 在头文件person_item.h中定义好结构体;在common.h 中定义通用配置
  • 在源文件person_main.cpp的主函数中,参照主流程图实现主体流程

2、填充具体功能

  • 在menus.cpp中定义绘制各级菜单的函数;函数声明放在common.h里
  • 在read_input.cpp中定义读取键盘输入的函数,包括读取菜单选择、读取输入的金额数、读取确认退出信息;这些函数都需要对输入做合法性校验,并返回正确的值;函数声明放在common.h里
  • 在operations.cpp中定义从文件加载数据、添加、查询、修改对应的函数。

3、各功能的细分

1)添加功能

  • 首先需要读取键盘输入的姓名、电话和备住址信息;
  • 然后将其保存在一个Person对象中,并添加到vector,同时写入文件

2)显示功能

  • 需要用for循环遍历整个vector
  • 通过条件筛选需要的联系人打印输出,并统计总和

2)修改功能

  • 需要用for循环遍历整个vector
  • 通过条件匹配到联系人,对数据进行更改
  • 将更改后的数组重新写入文件

4)退出功能

  • 提示用户确认退出
  • 用户确认后,更改循环标志位,退出循环

4.3 代码具体实现

4.3.1 定义头文件

首先可以在common.h 中,引入相应的库,定义出需要的全局变量,以及声明绘制菜单和读取键盘输入的通用函数。通用设置,通用功能性函数声明。

#pragma once
//通用设置,通用功能性函数声明
#include<iostream>			//输入输出
#include<string>			//字符串操作
#include<fstream>			//文件操作
#include<vector>
#include <algorithm>
using namespace std;

#define max 1000
#define OFFICE "办公"
#define MYSELF "个人"
#define FILENAME "book.txt"
//绘制菜单函数
void menus_main();
void menus_app();
void menus_show();
void menus_revise();
void menus_delete();

//读取键盘输入进行合法性校验的函数
char readMenuSelection(int options);
char readquitconfirm();

然后在person_item.h 定义核心数据结构:结构体 Person,并声明涉及到账目操作的函数。结构体定义,涉及账目操作的函数声明。

#pragma once
//结构体定义,涉及通讯录管理操作的函数声明
#include "common.h"

//设计联系人结构体
struct Person
{
	string PersonItem;	//类别,1、办公,2、个人
	string name;	//姓名
	string iphone;	//电话
	string address;	//单位
};

//针对通讯录数据进行操作的函数

//加载通讯录数据
void loadDataFromFile(vector<Person>& ps);


//添加
void person_add(vector<Person>& ps);
void insertIntoFile(const Person& p);
void person_addmyself(vector<Person>& ps);
void person_addOffice(vector<Person>& ps);
//显示
void person_show(vector<Person>& ps);
void showPerson(const vector<Person>& ps);
void showPerson(const vector<Person>& ps, const string PersonItem);
void printPerson(const Person& p);
//修改
void person_revise(vector<Person>& ps);
void change(vector<Person>& ps, string item);

4.3.2 实现主体流程

在源文件 person_main.cpp 的主函数中,参照主流程图实现主体流程。

代码如下:

#include "common.h"
#include "person_item.h"

int main() {
	//从文件加载数据
	vector<Person> ps;
	loadDataFromFile(ps);

	bool quit = false;
	while (!quit)
	{
		//1、显示主菜单
		menus_main();

		//2、读取键盘输入,并做合法性校验
		char key= readMenuSelection(5);
		switch (key)
		{
			
		case '1':			//1、添加联系人
			menus_app();	//添加菜单
			person_add(ps);	//添加功能
			break;
			
		case '2':			//2、显示联系人
			menus_show();	//显示菜单
			person_show(ps);		//显示功能
			break;
			
		case '3':			//3、修改联系人
			menus_revise();	//修改菜单
			person_revise(ps);	//修改功能
			break;
			

		case '4':			//退出
			cout << "确认退出? (Y/N)";
			if (readquitconfirm() == 'Y')
				quit = true;
			break;
		default:
			break;
		}

	}


}

4.3.3 绘制菜单

menus.cpp 中定义绘制各级菜单的函数;对应的函数声明放在 common.h里。

代码如下:

//菜单函数
#include "common.h"

//主菜单
void menus_main() {
	system("cls");		//清屏
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|***************     1 添加联系人     ********************|" << endl;
	cout << "|***************     2 显示联系人     ********************|" << endl;
	cout << "|***************     3 修改联系人     ********************|" << endl;
	cout << "|***************     4 退      出     ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 4):";
}
//添加联系人菜单
void menus_app() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择添加联系人类别 ********************|" << endl;
	cout << "|***************   1  办        公    ********************|" << endl;
	cout << "|***************   2  个        人    ********************|" << endl;
	cout << "|***************   3  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 3):";
	cin.get();
}
//显示联系人菜单
void menus_show() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择显示联系人类别 ********************|" << endl;
	cout << "|***************   1  全        部    ********************|" << endl;
	cout << "|***************   2  办        公    ********************|" << endl;
	cout << "|***************   3  个        人    ********************|" << endl;
	cout << "|***************   4  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 4):";
}
//修改联系人菜单
void menus_revise() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择修改联系人类别 ********************|" << endl;
	cout << "|***************   1  办        公    ********************|" << endl;
	cout << "|***************   2  个        人    ********************|" << endl;
	cout << "|***************   3  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 3):";
}
//删除联系人菜单
void menus_delete() {
	cout << "-----------------------------------------------------------" << endl;
	cout << "|                                                         |" << endl;
	cout << "|*************** 请选择删除联系人类别 ********************|" << endl;
	cout << "|***************   1  办        公    ********************|" << endl;
	cout << "|***************   2  个        人    ********************|" << endl;
	cout << "|***************   3  退        出    ********************|" << endl;
	cout << "|_________________________________________________________|" << endl;

	cout << "\n请选择(1 - 3):";
}

4.3.4 读取键盘输入

read_input.cpp 中定义读取键盘输入的函数,包括读取菜单选择、读取输入的金额数、读取确认退出信息;这些函数都需要对输入做合法性校验,并返回正确的值。

代码如下:

//读取输入函数
#include "common.h"

//读取键盘输入的菜单选项,进行合法性校验
char readMenuSelection(int options) {
	string str;

	while (true)
	{
		getline(cin, str);
		//进行合法化校验
		if (str.size() != 1 || str[0] - '0' <= 0 || str[0] - '0' > options)
			cout << "输入错误,请重新选择:";
		else
			break;
	}

	//输入合法
	return str[0];

}


//读取确认退出信息,并进行合法性校验
char readquitconfirm() {
	string str;
	while (true)
	{
		getline(cin, str);
		//进行合法性校验
		if (str.size() != 1 || toupper(str[0]) != 'Y' && toupper(str[0]) != 'N')
			cout << "输入错误,请重新选择:";
		else
			break;
	}
	//输入合法
	return toupper(str[0]);
}

4.3.5 实现具体操作功能

operations.cpp 中,定义从文件加载数据、添加、显示、修改等操作对应的函数。

  1. (1)从文件加载数据

loadDataFromFile():逐行 读取文件中数据,按空格将每个词赋值给Person中的每个成员,得到一个Person对象,并把它保存到数组中。

由于数组长度是固定的,这里应该用可变长的容器对象vector< Person**>来保存**;为了能够在函数中修改vector,应该把它的引用作为函数参数传入。

  1. (2)添加功能

首先需要读取键盘输入的金额和备注信息;然后将其保存在一个AccountItem 对象中,并添加到数组,同时写入文件。

person_add():添加联系人主体函数

person_addOffice():记录办公类联系人,将数据添加到vector中,并调用insertIntoFile()函数,写入文件做持久化保存

person_addmyself():记录个人类联系人,将数据添加到vector中,并调用insertIntoFile()函数,写入文件做持久化保存

insertIntoFile():将一条账目写入文件中。

  1. (3)显示功能

需要用for循环遍历整个数组,通过条件 筛选需要的联系人打印输出,并统计总和。

person_show():查询主体函数

showPerson():显示通讯录联系人

  1. (4)修改功能

需要用for循环遍历整个数组,通过条件 筛选需要的联系人进行更改,更改后使用for循环再次遍历整个数组,重新写入文件中做持久化保存。

person_revise():修改联系人主体函数

change():遍历数组找到联系人进行更改,将更改后的结果重新写入文件

具体代码如下:

//涉及通讯录操作的函数
#include "common.h"
#include "person_item.h"

//读取文件中的账目数据
void loadDataFromFile(vector<Person>& ps) {
	ifstream input(FILENAME);

	//逐行读取每一条数据,包装成Person对象
	Person p;
	string line;
	while (input >> p.PersonItem >> p.name >> p.iphone >> p.address) {
		ps.push_back(p);
	}
	input.close();
}

//----------------------------------------添加-----------------------------------------------
//添加操作
void person_add(vector<Person>& ps) {
	//读取键盘选择,并做合法化校验
	char key = readMenuSelection(3);
	switch (key)
	{
	case '1':			//办公
		person_addOffice(ps);
		break;
	case '2':			//个人
		person_addmyself(ps);
		break;
	case '3':			//返回主菜单
		break;
	default:
		break;
	}
}

//记录办公类别通讯录
void person_addOffice(vector<Person>& ps) {
	//新构建一个Person对象
	Person p;

	//类型已经确定,就是办公
	p.PersonItem = OFFICE;

	//与用户交互,键盘输入姓名,电话,住址
	cout << "\n请输入姓名:";
	getline(cin, p.name);
	cout << "\n请输入电话:";
	getline(cin, p.iphone);
	cout << "\n请输入住址:";
	getline(cin, p.address);

	//添加到vector中
	ps.push_back(p);
	//写入文件做持久化保存
	insertIntoFile(p);
	//显示成功信息
	cout << "\n----------------添加成功----------------\n";
	cout << "\n请按回车键返回主菜单......." << endl;

	string str;
	getline(cin, str);

}	

//记录个人类别通讯录
void person_addmyself(vector<Person>& ps) {
	//新构建一个Person对象
	Person p;

	//类型已经确定,就是办公
	p.PersonItem = MYSELF;

	//与用户交互,键盘输入姓名,电话,住址
	cout << "\n请输入姓名:";
	getline(cin, p.name);
	cout << "\n请输入电话:";
	getline(cin, p.iphone);
	cout << "\n请输入住址:";
	getline(cin, p.address);

	//添加到vector中
	ps.push_back(p);
	//写入文件做持久化保存
	insertIntoFile(p);
	//显示成功信息
	cout << "\n----------------添加成功----------------\n";
	cout << "\n请按回车键返回主菜单......." << endl;

	string str;
	getline(cin, str);
}


//将一条数据写入文件中
void insertIntoFile(const Person& p) {
	//创建一个ofstream对象,第二个参数表示以追加的方式进行写入
	ofstream output(FILENAME, ios::out | ios::app);

	output << p.PersonItem << "\t" << p.name << "\t" << p.iphone << "\t" << p.address << endl;
	output.close();
}


//-------------------------------------显示----------------------------------------------------------------
void person_show(vector<Person>& ps) {
	//读取键盘选择,并做合法性校验
	char key = readMenuSelection(4);
	switch (key)
	{
	case '1':					//所有
		showPerson(ps);
		break;
	case '2':					//办公
		showPerson(ps,OFFICE);
		break;
	case '3':					//个人
		showPerson(ps,MYSELF);
		break;
	case '4':					//返回主菜单
		break;
	default:
		break;
	}
}

//显示所有通讯录
void showPerson(const vector<Person>& ps) {
	cout << "----------------------查询结果-----------------------" << endl;
	cout << "\n类型\t\t姓名\t\t电话\t\t住址\n" << endl;

	//遍历所有通讯录,并添加数目
	int count = 0;
	for (auto p : ps) {
		printPerson(p);
		++count;
	}
	//输出信息
	cout << "===========================================================" << endl;
	cout << "总联系人数:" << count << endl;
	cout << "\n请按回车键返回主菜单......" << endl;

	string str;
	getline(cin, str);
}

//函数重载,显示联系人
void showPerson(const vector<Person>& ps,const string PersonItem) {
	cout << "----------------------查询结果-----------------------" << endl;
	cout << "\n类型\t\t姓名\t\t电话\t\t\t住址\n" << endl;

	//遍历所有通讯录,并添加数目
	int count = 0;
	for (auto p : ps) {
		if (p.PersonItem != PersonItem)
			continue;
		printPerson(p);
		++count;
	}
	//输出信息
	cout << "===========================================================" << endl;
	cout << "总联系人数:" << count << endl;
	cout << "\n请按回车键返回主菜单......" << endl;

	string str;
	getline(cin, str);
}

//打印每一条通讯录数据
void printPerson(const Person& p) {
	cout << p.PersonItem << "\t\t" << p.name << "\t\t" << p.iphone << "\t\t" << p.address << endl;
}


//----------------------------------------修改---------------------------------------------------------------------------
//修改联系人主体函数
void person_revise(vector<Person>& ps) {
	//读取键盘选择,并做合法化校验
	char key = readMenuSelection(3);

	switch (key)
	{
	case '1':					//办公
		change(ps,OFFICE);

		break;
	case '2':					//个人
		change(ps,MYSELF);

		break;
	case '3':					//退出
		break;					
	default:
		break;
	}
}

//修改联系人操作函数
void change(vector<Person>& ps, string item) {
	cout << "请输入您要修改的联系人姓名:";
	string name;
	bool found = false;
	while (!found) {
		getline(cin, name);
		for (auto& p : ps) {
			if (p.PersonItem == item && p.name == name) {
				string changeNmae;
				string changeIphone;
				string changeAddress;
				cout << "请输入更改后的姓名:";
				getline(cin, changeNmae);
				p.name = changeNmae;
				cout << endl;
				cout << "请输入更改后的电话:";
				getline(cin, changeIphone);
				p.iphone = changeIphone;
				cout << endl;
				cout << "请输入更改后的住址:";
				getline(cin, changeAddress);
				p.address = changeAddress;
				cout << endl;
				found = true;
				break;
			}
		}
		if (!found) {
			cout << "未找到此联系人,请重新输入:" << endl;
		}
	}

	// 将更改写回到文件,	trunc模式会将文件中的数据清空,然后重新写入新的内容
	ofstream outputFile(FILENAME, ios::out | ios::trunc);
	if (outputFile.is_open()) {
		for (const auto& p : ps) {
			outputFile << p.PersonItem << "\t" << p.name << "\t" << p.iphone << "\t" << p.address << endl;
		}
		outputFile.close();
	}
	else {
		cerr << "无法打开文件进行写入." << endl;
	}

	// 显示成功信息
	cout << "----------------修改成功----------------" << endl;
	cout << "请按回车键返回主菜单......." << endl;
	string str;
	getline(cin, str);
}

至此,我们就完成了对通讯录管理的设计与实现啦,你也快去试一下吧~

相关推荐
飞飞-躺着更舒服几秒前
【QT】实现电子飞行显示器(改进版)
开发语言·qt
武昌库里写JAVA16 分钟前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
CYBEREXP200833 分钟前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
ZSYP-S1 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos1 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos1 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习1 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
程序员_三木1 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊1 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法