第一章 设计要求
1.1 问题描述 使用C或C++实现一个通讯录管理系统,具有实现通讯录的建立和输出、通讯者的插入、 删除和查询等几种操作功能。 1.2 需求分析 1.2.1 需求概述 通讯录管理系统是一个比较实用的小型管理系统。随着生活节奏的加快,人们追求丰富便捷的电子产品体验,因此也提高对便捷方式的喜爱。当今社会下人们的交际范围越来越广泛,通讯录系统的出现,方便了人们存储通讯录,便于人们日常的交际。为方便广大人们对通讯管理系统的使用,本设计采用控制语句来改变程序各项功能的实现。主菜单部分主要解决的问题是程序开始选择问题,以及循环选择问题,其余各部分根据其实现功能完成代码设计。 1.2.2 环境需求 本课程设计需要的设备为硬件要求和软件配置要求具体要求如下: (1)硬件要求:一台计算机。 (2)软件配置:Windows、MinGW 32、CLion2020.2 IDE编译工具。 1.2.3 功能需求 本课程设计是利用数据结构的相关知识在 MinGW编译工具和CLion IDE下用 C++ 实现一个通讯录管理系统。具有实现通讯录的建立和输出、通讯者的插入、删除和查询等几种操作功能。
第二章 概要设计
2.1 主界面设计 主界面设计代码如下图2.1.1所示:
图 2.1.1主界面代码图
主界面设计图如下图2.1.2所示:
图 2.1.2 主界面图 2.2 存储结构设计 声明一个结构体作为链表单个节点,其中存储的内容有学号、姓名、出生日期、电话号和家庭住址,这些节点利用指针连接起来。存储结构设计代码如下图2.2所示:
图 2.2存储结构设计图 2.3 系统功能设计 (1) 菜单功能:输出菜单界面,并提供一个菜单来实现各个函数的调用以及提示用户。 (2) 添加功能:包含输入功能,第一次可以一次完成若干条记录的输入,此后可每次完成一条记录的输入,增加通讯录的基本信息。 (3) 修改功能:根据联系人的名字,修改指定联系人的信息。 (4) 删除功能:根据联系人的名字,删除指定联系人的信息。 (5) 查询功能:根据联系人的名字,查找指定联系人的信息并输出。 (6) 显示功能:将目前所有的联系人的信息输出。 (7) 读取功能:从保存的文件中读取通讯录信息; (8) 保存功能:保存现有通讯录到文件中,防止程序退出数据丢失; (9) 关闭功能:退出程序。
第三章 模块设计
3.1 模块设计 3.1.1 功能模块设计 系统的功能模块包括通讯录链表的建立、通讯者结点的插入、通讯者结点的查询、通讯者结点的删除、通讯者结点的修改、通讯录链表的输出、向/从文件中写入或读取通讯录和退出通讯录管理系统,如下图3.1.1所示: 图3.1.1 功能图
3.1.2 系统流程图 系统流程如下图3.1.2所示:
图 3.1.2系统流程图 3.2 系统子程序及功能设计 系统子程序功能设计一览表如下表3.2.1:
函数名 | 功能 |
---|
void Menu(); | 显示菜单 | Node *Create(); | 新建通讯录链表 | bool InsertStudent(Node *head); | 插入学生信息 | bool DeleteStudent(Node *head, const string& id); | 删除学生信息 | Node *ReadFromFile(); | 从文件中读取通讯录 | bool WriteToFile(Node *head); | 向文件写入通讯录 | Node * FindStudent(Node *head, const string& id); | 查询学生信息(学号) | Node* FindStuInformation(const string& pName, Node* pHead); | 查询学生信息(姓名) | void PrintStudent(Node *head); | 输出全部学生信息 | void PrintStudentSingle(Node *pNode); | 输出单个学生节点信息 | bool ChangeInformation(Node* pNode); | 修改学生信息 | void SYS_CONTROL(); | 系统控制函数 |
表3.2.1 功能函数一览表 (1) 创建通讯录方法如下图3.2.1所示: 图3.2.1 Create方法
(2) 删除信息方法如下图3.2.2所示:
图3.2.2 deletestudent方法 (3) 查询学生信息方法如下图3.2.3所示:
图3.2.3 Findstudent方法 (4) 插入学生信息方法如下图3.2.4所示:
图3.2.4 Insertstudent (5) 打印学生信息功能如下图3.2.5所示: 图3.2.5 Printstudent
(6) 从文件中读取通讯录功能如下图3.2.6所示:
图3.2.6 Readfromfile (7) 保存通讯录功能如下图3.2.7所示:
图3.2.7 Writeinfile
(8) 修改功能流程图如下图3.2.8所示: 图3.2.8 修改功能流程图
3.3 函数主要调用关系图
函数主要调用关系如下图3.3所示: 图3.3.1 函数主要关系调用图
第四章 详细设计
4.1 数据定义 系统的数据定义如下图4.1所示:图4.1 链表节点数据定义图 4.2 系统主要子程序详细设计 4.2.1 通讯录链表建立
Node *Create() {
int i = 1;
char ch = 'Y';
Node *head = new Node();
if(nullptr == head) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
head->next = nullptr;
while(ch == 'Y'||ch == 'y') {
cout << "请输入第" << i << "位学生的学号、姓名、出生日期、姓别(1:表示是男生,0:表示是女生)、电话和地址(以空格隔开):" << endl;
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
cin >> newNode->id >> newNode->name >> newNode->birthday >> newNode->sex >> newNode->phone >> newNode->address;
newNode->next = nullptr;
newNode->next = head->next;
head->next = newNode;
i++;
cout << "是否继续输入学生的通讯录信息?(Y/N):";
cin >> ch;
}
return head;
}
4.2.2 插入节点学生信息
bool InsertStudent(Node *head) {
cout << "请输入学生的学号、姓名、出生日期、姓别(1:表示是男生,0:表示是女生)、电话和地址(以空格隔开):" << endl;
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
cin >> newNode->id >> newNode->name >> newNode->birthday >> newNode->sex >> newNode->phone >> newNode->address;
newNode->next = head->next;
head->next = newNode;
return true;
}
4.2.3 删除节点学生信息
bool DeleteStudent(Node *head, const string& id) {
Node *prev = head;
Node *p = head->next;
while(p != nullptr && p->id != id) {
prev = p;
p = p->next;
}
if(p == nullptr)return false;
prev->next = p->next;
delete p;
p = nullptr;
return true;
}
4.2.4 查询节点学生信息
Node* FindStudent(Node *head, const string& id) {
Node *p = head;
int count = 0;
while(p != nullptr && id != p->id) {
p = p->next;
++count;
}
for (int i=0;i<count-1;i++)
{
head = head->next;
}
if(p == nullptr)return p; else return head;
}
Node* FindStuInformation(const string& pName, Node* pHead) {
Node *p = pHead;
int count = 0;
while(p != nullptr && pName != p->name) {
p = p->next;
++count;
}
for (int i=0;i<count-1;i++)
{
pHead = pHead->next;
}
if(p == nullptr)return p; else return pHead;
}
4.2.5 修改节点学生信息
bool ChangeInformation(Node *pNode) {
Node* p = pNode->next;
int ch = -1;
string str = "INIT";
cout<<"按编号选择需要修改的项目,【按0】退出"<<endl;
while(ch != 0){
cin>>ch;
switch (ch) {
case 1:
cout<<"修改学号为:"<<endl;
cin>>str;
p->id = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 2:
cout<<"修改姓名为:"<<endl;
cin>>str;
p->name = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 3:
cout<<"修改出生日期为:"<<endl;
cin>>str;
p->birthday = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 4:
p->sex = !(p->sex);
str = "Changed by [4](sex)";
cout<<"性别已修改!"<<endl;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 5:
cout<<"修改电话号为:"<<endl;
cin>>str;
p->phone = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 6:
cout<<"修改家庭地址为:"<<endl;
cin>>str;
p->address = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 0:
break;
default:
cout<<"选择错误,请重新选择!"<<endl;
}
}
if ( str == "INIT")return false; else return true;
}
4.2.6 保存通讯录
bool WriteToFile(Node *head) {
if (head == nullptr){PLCR return false;}
Node *p = head->next;
int ch = 0,flag = 1;
while (flag){
cout<<"[0]覆盖原有通讯录 [1]添加到原有通讯录"<<endl;
cin>>ch;
switch (ch) {
case 0:
flag = 0;
break;
case 1:
flag = 0;
break;
default:
cout<<"选择错误"<<endl;
}
}
ofstream outFile;
if(ch){outFile.open("StuMessageFile/student.txt",ios::out|ios::app);}else{outFile.open("StuMessageFile/student.txt", ios::out);};
if(!outFile)
{
cout << "Error: opening file fail" << endl;
exit(1);
}
while(p != nullptr) {
outFile << p->id << " " << p->name << " " << p->birthday << " " << p->sex << " " << p->phone << " " << p->address << endl;
p = p->next;
}
return true;
}
4.2.7 读取通讯录
Node *ReadFromFile() {
Node *head = new Node();
if(nullptr == head) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
ifstream inFile;
inFile.open("StuMessageFile/student.txt", ios::in);
if(!inFile.is_open())
{
cout << "Error: opening file fail" << endl;
exit(1);
}
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
while(!inFile.eof()) {
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
inFile >> newNode->id >> newNode->name >> newNode->birthday >> newNode->sex >> newNode->phone >> newNode->address;
newNode->next = head->next;
head->next = newNode;
}
inFile.close();
head = head->next;
return head;
}
第五章 测试分析
5.1 遇到的问题及解决方法
- 问题:
(1) 在未创建或未读取通讯录的情况下,选择其他功能程序会崩溃; (2) 保存通讯录后,覆盖原有学生信息文件; (3) 查询并打印时,打印出被查询学生后的所有学生信息; (4) 打印通讯录所有学生信息时,未存储任何信息的头结点被打印; (5) 程序测试时,控制台乱码显示中文字符。 - 解决办法:
(6) 在主程序Switch()case……中添加头结点判断; (7) 修改文件的打开方式为:追加写入方式; (8) 重写PrintStudentSingle函数,取消while(p != nullptr)循环条件; (9) 将头结点跳过,从下一个开始,head = head->next; (10) 编写程序选择UTF-8编码格式,控制台调试选择GBK编码。
5.2 经验和体会 (1)通过本次项目使我们进一步理解和掌握课堂上所学各种基本抽象数据类型的逻辑结构、存储结构和操作实现算法,以及它们在程序中的使用方法。 (2)使我们掌握软件设计的基本内容和设计方法,并培养了我们进行规范化软件设计的能力。 (3)使我们能掌握使用各种计算机资料和有关参考资料,提高我们进行程序设计的基本能力。
5.3 测试功能展示 (1) 主界面如下图5.3.1所示: 图5.3.1 主界面
(2) 通讯录链表建立(1. 新建学生通讯录)功能测试如下图5.3.2所示: 图5.3.2 新建通讯录
(3) 插入节点学生信息(2. 在通讯录插入学生信息)测试如下图5.3.3所示: 图5.3.3 插入信息
(4) 删除节点学生信息功能(3. 在通讯录删除学生信息)测试如下图5.3.4所示: 图5.3.4 删除信息
(5) 查询节点学生信息功能(6. 在通讯录按学号查询信息)测试如下图5.4.5所示: 图5.4.5 查询信息
(6) 修改节点学生信息(8. 修改学生信息)功能测试如下图5.4.6所示:
图5.4.6 修改学生信息
(7) 保存通讯录(5. 向文件写入通讯录信息)功能如下图所示: 图5.4.7 保存通讯录
(8) 读取通讯录(4. 从文件读取通讯录信息)功能测试如下图5.4.8所示; 图5.4.8 读取通讯录
第六章 源程序清单
6.1 main.cpp
#include "student.h"
using namespace std;
int main() {
SYS_CONTROL();
return 0;
}
6.2 student.cpp
#include "student.h"
void Menu() {
SYSTEMTIME sys;
GetLocalTime(&sys);
cout << "\t\t************************************\n\t\t**^_^欢迎使用学生通讯录管理系统^_^**\n\t\t************************************\n\t\t\t "<<sys.wYear<<"年"<<sys.wMonth<<"月"<<sys.wDay<<"日";cout<<"\n****************************************************************\n**\t\t\t通讯录管理主菜单\t\t **\n**------------------------------------------------------------**\n";
cout << "**| 1.新建学生通讯录\t\t 5.向文件写入通讯录信息 |**\n";
cout << "**| 2.在通讯录插入学生信息\t 6.在通讯录按学号查询信息 |**\n";
cout << "**| 3.在通讯录删除学生信息\t 7.在屏幕打印全部学生信息 |**\n";
cout << "**| 4.从文件读取通讯录信息\t 8.修改学生信息\t\t |**\n**------------------------------------------------------------**\n****************************************************************\n";
cout << "**\t\t请输入选择(1-8):【0】退出系统 **\n****************************************************************\n";
}
Node *Create() {
int i = 1;
char ch = 'Y';
Node *head = new Node();
if(nullptr == head) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
head->next = nullptr;
while(ch == 'Y'||ch == 'y') {
cout << "请输入第" << i << "位学生的学号、姓名、出生日期、姓别(1:表示是男生,0:表示是女生)、电话和地址(以空格隔开):" << endl;
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
cin >> newNode->id >> newNode->name >> newNode->birthday >> newNode->sex >> newNode->phone >> newNode->address;
newNode->next = nullptr;
newNode->next = head->next;
head->next = newNode;
i++;
cout << "是否继续输入学生的通讯录信息?(Y/N):";
cin >> ch;
}
return head;
}
bool InsertStudent(Node *head) {
cout << "请输入学生的学号、姓名、出生日期、姓别(1:表示是男生,0:表示是女生)、电话和地址(以空格隔开):" << endl;
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
cin >> newNode->id >> newNode->name >> newNode->birthday >> newNode->sex >> newNode->phone >> newNode->address;
newNode->next = head->next;
head->next = newNode;
return true;
}
bool DeleteStudent(Node *head, const string& id) {
Node *prev = head;
Node *p = head->next;
while(p != nullptr && p->id != id) {
prev = p;
p = p->next;
}
if(p == nullptr)return false;
prev->next = p->next;
delete p;
p = nullptr;
return true;
}
Node *ReadFromFile() {
Node *head = new Node();
if(nullptr == head) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
ifstream inFile;
inFile.open("StuMessageFile/student.txt", ios::in);
if(!inFile.is_open())
{
cout << "Error: opening file fail" << endl;
exit(1);
}
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
while(!inFile.eof()) {
Node *newNode = new Node();
if(nullptr == newNode) {
cerr << "内存空间分配失败" << endl;
exit(1);
}
inFile >> newNode->id >> newNode->name >> newNode->birthday >> newNode->sex >> newNode->phone >> newNode->address;
newNode->next = head->next;
head->next = newNode;
}
inFile.close();
head = head->next;
return head;
}
bool WriteToFile(Node *head) {
if (head == nullptr){PLCR return false;}
Node *p = head->next;
int ch = 0,flag = 1;
while (flag){
cout<<"[0]覆盖原有通讯录 [1]添加到原有通讯录"<<endl;
cin>>ch;
switch (ch) {
case 0:
flag = 0;
break;
case 1:
flag = 0;
break;
default:
cout<<"选择错误"<<endl;
}
}
ofstream outFile;
if(ch){outFile.open("StuMessageFile/student.txt",ios::out|ios::app);}else{outFile.open("StuMessageFile/student.txt", ios::out);};
if(!outFile)
{
cout << "Error: opening file fail" << endl;
exit(1);
}
while(p != nullptr) {
outFile << p->id << " " << p->name << " " << p->birthday << " " << p->sex << " " << p->phone << " " << p->address << endl;
p = p->next;
}
return true;
}
Node* FindStudent(Node *head, const string& id) {
Node *p = head;
int count = 0;
while(p != nullptr && id != p->id) {
p = p->next;
++count;
}
for (int i=0;i<count-1;i++)
{
head = head->next;
}
if(p == nullptr)return p; else return head;
}
void PrintStudent(Node *head) {
Node *p = head->next;
while(p != nullptr) {
cout << "学号:" << p->id << " 姓名:" << p->name << " 出生日期:" << p->birthday;
if(p->sex == 1)
cout << " 性别:男";
else
cout << " 性别:女";
cout << " 电话:" << p->phone << " 地址:" << p->address << endl;
p = p->next;
}
}
void PrintStudentSingle(Node *pNode) {
Node* p = pNode->next;
cout << "【1】学号:" << p->id << " 【2】姓名:" << p->name << " 【3】出生日期:" << p->birthday;
if(p->sex == 1)
cout << " 【4】性别:男";
else
cout << " 【4】性别:女";
cout << " 【5】电话:" << p->phone << " 【6】地址:" << p->address << endl;
}
Node* FindStuInformation(const string& pName, Node* pHead) {
Node *p = pHead;
int count = 0;
while(p != nullptr && pName != p->name) {
p = p->next;
++count;
}
for (int i=0;i<count-1;i++)
{
pHead = pHead->next;
}
if(p == nullptr)return p; else return pHead;
}
void FindAll(Node* pNode,const string& pName)
{
Node* p = pNode;
while (pNode != nullptr)
{
if (pName == pNode->name)PrintStudentSingle(p);
p = pNode;
pNode = pNode->next;
}
}
bool ChangeInformation(Node *pNode) {
Node* p = pNode->next;
int ch = -1;
string str = "INIT";
cout<<"按编号选择需要修改的项目,【按0】退出"<<endl;
while(ch != 0){
cin>>ch;
switch (ch) {
case 1:
cout<<"修改学号为:"<<endl;
cin>>str;
p->id = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 2:
cout<<"修改姓名为:"<<endl;
cin>>str;
p->name = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 3:
cout<<"修改出生日期为:"<<endl;
cin>>str;
p->birthday = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 4:
p->sex = !(p->sex);
str = "Changed by [4](sex)";
cout<<"性别已修改!"<<endl;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 5:
cout<<"修改电话号为:"<<endl;
cin>>str;
p->phone = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 6:
cout<<"修改家庭地址为:"<<endl;
cin>>str;
p->address = str;
cout<<"继续按编号选择需要修改的项目,或【按0】退出"<<endl;
break;
case 0:
break;
default:
cout<<"选择错误,请重新选择!"<<endl;
}
}
if ( str == "INIT")return false; else return true;
}
void SYS_CONTROL() {
SetConsoleTitle("学生通讯录管理系统");
int choice;
string name;
string id;
bool Changed = false;
bool finished = false;
Node *head = nullptr;
while(!finished) {
Menu();
cin >> choice;
switch(choice) {
case 1:
head = Create();
break;
case 2:
if(head == nullptr){PLCR cout<<"【提示】:<插入失败>"<<endl;}else {
if (InsertStudent(head))
cout << "插入学生信息成功!" << endl;
else
cout << "插入学生信息失败!" << endl;
}
break;
case 3:
if(head == nullptr){PLCR}else {
cout << "请输入要删除的学生通讯录信息的学号:";
cin >> id;
if (DeleteStudent(head, id))
cout << "删除成功!" << endl;
else
cout << "删除失败!" << endl;
}
break;
case 4:
if (head = ReadFromFile())
{
cout<<"【提示】:<读取成功>"<<endl;
}else cout<<"【提示】:<读取失败>"<<endl;
break;
case 5:
if (WriteToFile(head))
{
cout<<"【提示】:<写入成功>"<<endl;
} else{cout<<"【提示】:<写入失败>"<<endl;}
break;
case 6:
if (head == nullptr){PLCR}else{
cout << "请输入要查找的学生信息的学号:";
cin >> id;
if(FSPTRID != nullptr) {
PrintStudentSingle(FSPTRID);
break;
}else{
cout << "查无此人!" << endl;
}
}
break;
case 7:
if (head == nullptr){PLCR }else {
PrintStudent(head);
}
break;
case 8:
if (head == nullptr){PLCR }else {
cout << "请输入要修改的学生信息的姓名:";
cin >> name;
if(FindStuInformation(name,head) != nullptr) {
Node* pTemp = FindStuInformation(name,head);
PrintStudentSingle(pTemp);
Changed = ChangeInformation(pTemp);
if (Changed){cout<<"修改后该生通讯录信息为:"<<endl;PrintStudentSingle(pTemp);}else cout<<"【提示】:未修改任何信息"<<endl;
}else{
cout << "查无此人!" << endl;
}
}
break;
case 0:
finished = true;
break;
default:
system("cls");
cout << "输入选择错误,请重新输入!" << endl;
}
}
}
6.3 student.h
#ifndef STUDENT_MANGER_SYS_STUDENT_H
#define STUDENT_MANGER_SYS_STUDENT_H
#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
#define FSPTRID FindStudent(head, id)
#define PLCR system("cls");cout<<"【提示】:<请先读取或者创建通讯录>"<<endl;
using namespace std;
struct Node {
string id;
string name;
string birthday;
int sex;
string phone;
string address;
Node *next;
};
void Menu();
Node *Create();
bool InsertStudent(Node *head);
bool DeleteStudent(Node *head, const string& id);
Node *ReadFromFile();
bool WriteToFile(Node *head);
Node * FindStudent(Node *head, const string& id);
void PrintStudent(Node *head);
void PrintStudentSingle(Node *pNode);
Node* FindStuInformation(const string& pName, Node* pHead);
bool ChangeInformation(Node* pNode);
void SYS_CONTROL();
void FindAll(Node* pNode,const string& pName);
#endif
第七章 用户手册
7.1 操作步骤 7.1.1 创建通讯录 (1)按1,创建通讯录,按照提示输入学生信息,输入完成后,选择是否继续插入。 (2)按7,在屏幕中打印已有的学生信息。 (3)按6,查询已有的学生信息。 (4)按3,删除学生信息。 (5)按2,根据提示插入学生信息。 (6)按5,保存该通讯录。 (7)按8,退出系统。 7.1.2 读取通讯录 (1)按4,从文件中读取已保存的通讯录。 (2)按7,在屏幕中打印已有的学生信息。 (3)按6,查询已有的学生信息。 (4)按3,删除学生信息。 (5)按2,根据提示插入学生信息。 (6)按5,保存该通讯录。 (7)按8,退出系统。
|