大二上 数据结构课设--双链表通讯录 c++
一、前言
其实这次的数据结构课设写的不太好,因为学校提前放假,写课设和考试在一周之内了,本来想写飞机订票系统的,但是还是只能把综合实验改了改就交上去了。
1、运行环境
本程序使用c++编写,在vs2019上运行通过,最初使用vscode编写,但是由于设置控制台标题的函数有问题,就换到vs2019上用了
二、 实验内容
设计一个简单个人电话号码查询系统
1. 问题描述
人们在日常中经常要查找某个人或某个单位的电话号码,本实验将实现一个简单的个人电话号码查询系统,根据用户输入信息(例如姓名等)进行快速查询。
2、基本要求
(1)在外存上,用文件保存电话号码信息;
(2)在内存中,设计数据结构存储电话信息;
(3)提供查询功能;根据姓名实现快速查询;
(4)提供其他功能,例如插入、删除、修改等;
三、本程序实现的功能
1.主要功能
(1)利用双向循环带头链表存储记录
(2)实现了简单的控制台记录列表翻页效果
(3)实现了增加、依据序号删除的功能
(4)实现了根据任意关键字查找记录的功能(注:目前只返回第一个匹配结果)
(5)实现了根据序号修改任意单条记录的功能
(6)实现了存储/读取的功能
(7)实现了根据任意关键字进行排序(升序/降序)的功能 —//使用冒泡排序
2.其他功能
(1)进行了对输入的一般检测,保证在一般情况下不会出问题
(2)使用控制台相关函数修改了界面大小和控制台文字颜色,比较好看
(3)写了记录的拷贝函数
(4)重载了结构体的输入运算符,方便读取信息和后续修改
(5)重载了输出运算符,方便修改,用了left/right/setw进行控制输出格式
(6)进行了一些边界值的测试,保证一般情况下不会出现问题
四、实验源码与运行结果
1.源代码
(1)funcs.h
#pragma once
#include<iostream>
#include<Windows.h>
using namespace std;
int ckerror()
{
if (cin.fail())
{
cin.clear();
cin.ignore(99999, '\n');
return 1;
}
return 0;
}
void setCursorPos(COORD curpos)
{//固定点位输出
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);//获得窗口句柄
SetConsoleCursorPosition(screen, curpos);//两个参数分别是指定哪个窗体,具体位置
}
void setCursorPos(short x,short y)
{//固定点位输出
COORD curpos = { x,y };
setCursorPos(curpos);
}
(2)main.cpp
#include<iostream>//输入输出
#include<iomanip>
#include<fstream>//文件
#include<Windows.h>
#include<stdlib.h>//调用system("cls")
#include<string.h>//需要使用string,strcmp();
#include<conio.h>//getch()
#include"funcs.h"
using namespace std;
const int g_max_record_per_page = 10;
struct TelNumber
{//电话存储结构体
TelNumber();
TelNumber(const TelNumber& newtel);
string name;//
string phone_number;
string mobile_number;
string email;
TelNumber* prev, * next;
};
TelNumber::TelNumber()
{
name = "0";
phone_number = "0";
mobile_number = "0";
email = "0";
prev = NULL;
next = NULL;
}
TelNumber::TelNumber(const TelNumber& newtel)
{
name = newtel.name;
phone_number = newtel.phone_number;
mobile_number = newtel.mobile_number;
email = newtel.email;
prev = NULL;
next = NULL;
}
istream& operator>>(istream& sin, TelNumber& telnum)
{//重载输入运算符
sin >> telnum.name
>> telnum.phone_number
>> telnum.mobile_number
>> telnum.email;
return sin;
}
ostream& operator<<(ostream& sout, TelNumber& telnum)
{//输出电话号码
if (telnum.name.compare("0") == 0)
sout <<left<<setw(20)<< "无名";
else
sout << left << setw(20)<<telnum.name;
if (telnum.phone_number.compare("0") == 0)
sout << left <<setw(20)<< "无";
else
sout << left << setw(20) << telnum.phone_number ;
if (telnum.mobile_number.compare("0") == 0)
sout << left << setw(19)<<"无";
else
sout << left << setw(19) << telnum.mobile_number ;
if (telnum.email.compare("0") == 0)
sout << left << setw(25) <<"无";
else
sout << left << setw(25) << telnum.email;
sout << right;
return sout;
}
class TelManager
{
public:
TelManager();
~TelManager();
int readData();//读取数据
int saveData();
int getMaxpage();
int getRecordNum() { return tel_saved_num; }
void showTelNumber(int record_id);
void showTelNumberList(int page);//
int addTelNumber(const TelNumber& newtel);//添加记录
int getTelNumberBy(int search_type, string search_key, TelNumber& search_result);//搜索记录,返回待查数据的序号 search——key:搜索的值,search_type:搜索的数据的类型 search_result:搜索的结果
int deleteTelNumber(int record_id);//删除记录,返回是1否0成功 record_id:待删除记录的序号
int changeData(int record_id, TelNumber new_record);//修改数据,返回是1否0成功 record_id:待修改记录的序号,new_record:要修改为什么
int sortData(int sort_key_type, int order_type);//以~排序数据 sort_key_type:排序依据 order_type:排序方向,1.升序 2降序 排序方法:
int clearData();
private:
void swap(TelNumber*& pnow, TelNumber*& pnext)
{//冒泡交换
pnow->prev->next = pnext;
pnext->next->prev = pnow;//外接内
pnow->next = pnext->next;
pnext->prev = pnow->prev;//内接外
pnext->next = pnow;
pnow->prev = pnext;//自己接自己
pnow = pnow->prev;
pnext = pnext->next;//指针复位
}
TelNumber* head;//存储结构第一个数据的指针
int tel_saved_num;//已保存数量
int max_page;
//SMALL_RECT
}Book;
TelManager::TelManager()
{//无参构造函数
head = new TelNumber;
head->prev = head;
head->next = head;
tel_saved_num = 0;
readData();
}
TelManager::~TelManager()
{//析构函数
head->prev->next = NULL;//断开连接
TelNumber* pnow = head, * pt;
while (pnow)
{
pt = pnow;
pnow = pnow->next;
delete pt;
}
}
int TelManager::readData()
{//读取数据
ifstream hasdata("telNumber.txt");//验证是否是第一次运行---验证telNumber.txt有没有
int telnumber_count;
TelNumber ttelnum;
if (!hasdata.is_open())
{//没有数据,重新创建文件
ofstream newfile("telNumber.txt");
newfile << 0; //输入记录个数
newfile.close();
return 0;
}
else
{//有文件
hasdata.close();
}
ifstream fin("telNumber.txt");
fin >> telnumber_count; //读取记录个数
for (int i = 0; i < telnumber_count; i++)
{
fin >> ttelnum;
addTelNumber(ttelnum);
}
getMaxpage();
return 1;
}
int TelManager::saveData()
{
ofstream fout("telNumber.txt");
fout << tel_saved_num << endl;
if (tel_saved_num == 0)
{
fout.close();
return 1;
}
TelNumber* pnow = head->next;
while (pnow != head)
{
fout << pnow->name << ' '
<< pnow->phone_number << ' '
<< pnow->mobile_number << ' '
<< pnow->email << endl;
pnow = pnow->next;
}
fout.close();
return 1;
}
int TelManager::getMaxpage()
{
if (tel_saved_num % g_max_record_per_page != 0)
{
max_page = tel_saved_num / g_max_record_per_page + 1;
}
else
{
max_page = tel_saved_num / g_max_record_per_page;
}
return max_page;
}
void TelManager::showTelNumber(int record_id)
{
if (record_id > tel_saved_num)
return;
TelNumber* pnow = head->next;
for (int i = 1; i < record_id; i++)
{
pnow = pnow->next;
}
cout << *pnow;
}
void TelManager::showTelNumberList(int current_page)
{
int record_no = 0;
cout << "----------------------------------------------------------------------------------------@" << endl;
cout << " 姓名 电话号码 手机号码 邮箱地址" << endl;
cout << "---------------------------------------------------------------------------------------@ |" << endl;
if (tel_saved_num == 0)
{
cout << "无数据...";
return;
}
for (int i = (current_page - 1) * g_max_record_per_page + 1; i <= current_page * g_max_record_per_page; i++)
{
if (i > tel_saved_num)
cout << endl;
else
{
cout << setw(2) << record_no + 1 << '.';
Book.showTelNumber(i);
cout << '|'<<endl;
cout << setw(90)<<'|'<<endl;
}
record_no++;
}
if (current_page > 1)
cout << "<上一页(8) ";
else
{
cout << " ";
}
cout << "第" << current_page << '/' << max_page << "页";
if (current_page < max_page)
cout << " (9)下一页>";
else
{
cout << " ";
}
}
int TelManager::addTelNumber(const TelNumber& newtel)
{//于末尾添加电话号
TelNumber* pnow = head->prev;
pnow->next = new TelNumber(newtel);
pnow->next->prev = pnow;
pnow->next->next = head;
head->prev = pnow->next;
tel_saved_num++;
getMaxpage();
return 1;
}
int TelManager::getTelNumberBy(int search_type, string search_key, TelNumber& search_result)
{
if (tel_saved_num == 0)
{
return 0;
}
TelNumber* pnow = head->next;
while (pnow != head)
{
switch (search_type)
{
case 1:
if (pnow->name.compare(search_key) == 0)
{
search_result = *pnow;
return 1;
}
break;
case 2:
if (pnow->phone_number.compare(search_key) == 0)
{
search_result = *pnow;
return 1;
}
break;
case 3:
if (pnow->mobile_number.compare(search_key) == 0)
{
search_result = *pnow;
return 1;
}
break;
case 4:
if (pnow->email.compare(search_key) == 0)
{
search_result = *pnow;
return 1;
}
break;
default:
break;
}
pnow = pnow->next;
}
return 0;
}
int TelManager::deleteTelNumber(int record_id)
{//删除记录,依照序号record_id
TelNumber* pnow = head->next;
for (int i = 1; i < record_id; i++)
{//找到要修改的数据,数据必存在,故不会删掉head
pnow = pnow->next;
}
pnow->prev->next = pnow->next;
pnow->next->prev = pnow->prev;
delete pnow;
tel_saved_num--;
getMaxpage();
return 1;
}
int TelManager::changeData(int record_id, TelNumber new_record)
{
TelNumber* pnow = head->next;
for(int count = 1;count < record_id;count++)
{
pnow = pnow->next;
}
if (new_record.name.compare("0") != 0)
{
pnow->name = new_record.name;
}
if (new_record.phone_number.compare("0") != 0)
{
pnow->phone_number = new_record.phone_number;
}
if (new_record.mobile_number.compare("0") != 0)
{
pnow->mobile_number = new_record.mobile_number;
}
if (new_record.email.compare("0") != 0)
{
pnow->email = new_record.email;
}
return 1;
}
int TelManager::sortData(int sort_key_type, int order_type)
{
if (tel_saved_num < 2)
return 1;
bool need_sort = 1;
int rest = tel_saved_num;
TelNumber* pnow = head->next, * pnext = head->next->next;
for (int i = 0; i < tel_saved_num -1; i++)
{
if (!need_sort)
break;
need_sort = 0;
pnow = head->next, pnext = head->next->next;
for (int j = 0; j < tel_saved_num - i - 1; j++)
{
switch (sort_key_type)
{//处理当前数据
case 1:
if ((pnow->name.compare(pnext->name) > 0 && order_type == 1) ||
(pnow->name.compare(pnext->name) < 0 && order_type == 2))
{//交换
swap(pnow, pnext);
need_sort = 1;
}
break;
case 2:
if ((pnow->phone_number.compare(pnext->phone_number) > 0 && order_type == 1) ||
(pnow->phone_number.compare(pnext->phone_number) < 0 && order_type == 2))
{//交换
swap(pnow, pnext);
need_sort = 1;
}
break;
case 3:
if ((pnow->mobile_number.compare(pnext->mobile_number) > 0 && order_type == 1) ||
(pnow->mobile_number.compare(pnext->mobile_number) < 0 && order_type == 2))
{//交换
swap(pnow, pnext);
need_sort = 1;
}
break;
case 4:
if ((pnow->email.compare(pnext->email) > 0 && order_type == 1) ||
(pnow->email.compare(pnext->email) < 0 && order_type == 2))
{//交换
swap(pnow, pnext);
need_sort = 1;
}
break;
default:
break;
}
pnow = pnow->next;
pnext = pnext->next;
}
}
return 1;
}
int TelManager::clearData()
{
TelNumber* pnow = head->next, * pt;
while (pnow != head)
{
pt = pnow;
pnow = pnow->next;
delete pt;
}
head->next = head;
head->prev = head;
tel_saved_num = 0;
getMaxpage();
return 1;
}
void drawMenu()
{
int page = 1;
int option = -1;
int search_type = 1, sort_type = 1, sort_order = 1;
int change_no = 1,delete_no = 1;
int clear_option = 1;
string search_key;
TelNumber newtel,search_result,changetel;
while (1)
{
system("cls");
cout << "---------------电话管理菜单-----------------@" << endl;
cout << "1.添加记录 2.搜索记录|" << endl;
cout << "3.排序记录 4.修改记录|" << endl;
cout << "5.保存记录 6.删除记录|" << endl;
cout << "7.清空记录 0.退出程序|" << endl;
cout << "--------------------------------------------@" << endl;
cout << "\n\n\n\n\n";
Book.showTelNumberList(page);
setCursorPos(0, 6);
cout << "请输入您的选择:";
cin >> option;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
switch (option)
{
case 1:
cout << "请输入新记录的姓名,电话号码,手机号码和邮箱地址,如无请输入0:" << endl;
cin >> newtel;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (Book.addTelNumber(newtel))
cout << "添加成功";
else
{
cout << "添加失败";
}
break;
case 2:
cout << "请选择您想搜索的关键词:1.姓名 2.电话号码 3.手机号码 4.邮箱:";
cin >> search_type;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (search_type < 1 || search_type >4)
{
cout << "您输入的数字有误";
break;
}
cout << "请输入您要查找的内容:";
cin >> search_key;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (Book.getTelNumberBy(search_type, search_key, search_result))
{
cout << "找到记录:" << endl;
cout << search_result;
}
else
{
cout << "没有找到记录";
}
break;
case 3:
cout << "请输入您想按照什么排序:1.姓名 2.电话号码 3.手机号码 4.邮箱:";
cin >> sort_type;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (sort_type < 1 || sort_type >4)
{
cout << "您输入的数字有误";
break;
}
cout << "请输入排序方式:1.升序 2.降序:";
cin >> sort_order;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (sort_order < 1 || sort_order >2)
{
cout << "您输入的数字有误";
break;
}
if (Book.sortData(sort_type, sort_order))
{
cout << "排序完成";
}
else
{
cout << "排序失败";
}
break;
case 4:
cout << "请输入想要修改的人的序号:";
cin >> change_no;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (change_no < 1 || change_no > 20)
{
cout << "您输入的数字有误";
break;
}
cout << "请输入新的数据,如果不需要修改就输入0,例如:0 0 10133224422 qwerrty@qqq.com意为修改后两项为给的值" << endl;;
cin >> changetel;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
Book.changeData(change_no + (page - 1) * g_max_record_per_page, changetel);
break;
case 5:
Book.saveData();
break;
case 6:
cout << "请输入您想删除的数据的序号:";
cin >> delete_no;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (delete_no < 1 || delete_no > 20|| (delete_no + (page - 1) * g_max_record_per_page) > Book.getRecordNum())
{
cout << "取消删除"<< endl;
break;
}
if (Book.deleteTelNumber(delete_no + (page - 1) * g_max_record_per_page))
{
cout << "删除成功";
if (Book.getMaxpage() < page)//若在最后一页删除后该页没有记录,翻到上一页
page = Book.getMaxpage();
}
else
{
cout << "删除失败";
}
break;
case 7:
cout << "您真的要清空吗?是1/否0";
cin >> clear_option;
cin.sync();//防止输入过多
if (ckerror())
{
cout << "您的输入有误";
continue;
}
if (clear_option == 1)
{
if (Book.clearData())
{
cout << "删除成功";
page = 1;
}
else
{
cout << "删除失败";
}
}
break;
case 8:
page = page - 1 < 1 ? 1 : page - 1;
break;
case 9:
page = page + 1 > Book.getMaxpage() ? Book.getMaxpage() : page + 1;
break;
case 0:
return;
default:
break;
}
if (option != 8 && option != 9)
_getch();
}
}
void initWnd()
{//初始化窗口
SetConsoleTitleW(L"电话查询系统<^-^> ------by 计算机19-1张逸豪"); //设置标题,LPCWSTR Long Pointer to Const Wide_char String
system("mode con cols=100 lines=35"); //26行,每行100字符
system("color 0B"); //设置
HWND hwnd = GetConsoleWindow();
LONG style = GetWindowLongPtrW(hwnd, GWL_STYLE); //取得窗口类型 (define long LONG,类型是长整数)
style &= ~(WS_MAXIMIZEBOX); /*位与去掉最大化按钮,WS意为WindowStyle,WS_MAXIMIZE = 0x00010000L
*此处是以16进制存储longint,longint 是32字节,最大值是2^31-1加一位符号,2^32正是16^8
*以longint的每一位存储窗口样式选项
*/
//?如果同位存0x08000000L和0x02000000L不冲突吗,在二进制是不同的位吗
style &= ~(WS_SIZEBOX); //去掉拖动修改大小,防止破坏结构,比较方便 WS_SIZEBOX = 0x00040000L
SetWindowLong(hwnd, GWL_STYLE, style);
ReleaseDC(hwnd, NULL); //释放空间
}
int main()
{
initWnd();
drawMenu();
return 0;
}
(3)测试数据
注:测试数据属于虚构
telNumber.txt
33
TirRal 28853412 11122235123 OrIWillx_x@tt.com
杨静晨 28779493 10549631188 SilenTMoorn@tt.com
田馨婷 51396575 10432411575 seenTea@sqr4.com
乔岚风 22123405 10524558275 mtnWindY@jio.com
戴昕靓 86651842 10734669335 CyanDawn@dai.com
慎啸 15881436 10846156162 CareYel@msg.com
高叔 74464893 10458465682 mnSealw@qqw.com
浦辈 33349628 10145648958 shuiche@me.com
毛示 86213374 10855655878 Mosssss@ao.com
刘浩广 72144016 10374296783 Howgone@yui.com
周景铄 22941669 10757683235 SunFunz@zzz.com
江天瑞 32396731 10320981991 TerryJTR@mso.com
马英耀 46899638 10632297190 YueYouUM@mma.com
许致远 32165089 10967987285 PreMiss2FAr@qwa.com
高可心 51540484 10645688888 Hicocox@wel.com
张之双 28027814 10655964888 shellTwozZ@mms.com
杨馥 39871919 10156484887 fufuYanf@waw.com
黄可 79490088 10156489887 KingNok@mms.com
朱姝 34403188 10998766555 facepowder@zzz.com
史鸾 48030475 10488985499 sheLoved@waw.com
养嘉悦 51810455 10489785698 YoungJerry@wat.com
文思雁 91898380 10459898578 TK_spArrow@wel.com
逢春桃 62833858 10778489566 xhsNoCore@hh.com
宁生文 71589247 10145898656 whyBeme@haw.com
储村 82920192 10564884872 SavaLLAge@ww.com
从泉 84745558 10156482978 1supona_Spring@qqw.com
逯茗 45842515 10484858878 hardtoread@name.com
林汉 87482592 10548487897 Linkeeee@wqw.com
彭晋 56521132 10486455212 PenGUI@pg.com
孟评 15213564 10548479887 PaoMain@ww.com
方荷 15656222 10100010659 VeFor_Her@msg.com
秦梦泽 39826327 10646898447 QinMengZe8447@ww.com
曹鸿 42255895 10548654174 YanCC2000@waw.com
2、运行结果:
如图,是程序运行的初始状态
如图,是有数据时进行查找的情况
五、总结
双链表这样用在查找太多数据的时有问题,其实可以记录当前的记录,直接传入最近10条数据之前的数据就能避免这种很慢的情况,但是因为时间有限,而且只是一次实验,所以就先不写出来了,这次实验里我做了一些尝试,感觉还不错。。
就是这样。