C++课程设计(简单图书管理系统)_c简单图书管理系统
C++课程设计(简单图书管理系统)由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“c简单图书管理系统”。
课 程 设 计
课程名称
C++课程设计 题目名称
图书管理系统
2016 年月 29 日
目录
一.设计内容与要求...............................................................................................................2 二.设计思路:.......................................................................................................................4 三.关键模块实现及主要代码...............................................................................................4 四.实验结果及分析.............................................................................................................13
五、心得体会.........................................................................................................................16
六、参考文献.........................................................................................................................17
一.设计内容与要求
(1)、设计一个图书管理系统,能够实现用户信息管理,图书信息的录入、查询、删除等功能。
(2)、实现用户管理,系统具有用户注册功能。管理员的账号和密码由系统指定,登录后可进行修改。一般用户注册后才能登录使用系统。管理员具有录入、查询、删除图书信息及一般用户的功能。一般用户只有图书查询功能。
(3)、登录功能,只有注册了的用户才能进入系统,没有注册的则有提示信息出现。
(4)、图书管理功能,管理员能够录入、查询、删除图书信息。(5)、图书查询功能,管理员及一般用户能够通过图书的基本信息,如图书作者、书名、出版社、出版日期等等,进行查询。
提示:上述为基本功能,可参考实际的系统进行扩展。
二.设计思路:
为了实现用户信息以及图书信息的管理,建立一个用户CUser类与图书信息CBoodata类,其中CUser类应该包含用户账号以及密码,CBookdata类应该包含图书作者,书名,出版社,出版日期。同时应该建立两个dat文件,一个为存储用户信息,一个用来储存图书信息。程序主要分为4个页面:(1)登录页面,由Cdialog类派生出CMyprojectDlg类,用来处理账号注册与用户登录。
(2)图书查询页面。由Cdialog类派生出CSearch类。包含读取所有图书数据响应函数,以及根据作者姓名或者书名来查询图书响应函数。
(3)管理员图书管理页面。由Cdialog类派生出CBookdata类。包含读取所有图书响应函数,添加图书响应函数,修改图书响应函数,删除图书响应函数。(4)用户信息管理页面,由Cdialog类派生出CUserdata类。包含读取所有用户信息响应函数,删除用户响应函数、添加用户响应函数、修改用户信息响应函数。
因为本人能力有限,而且自己是根据老师给的教程以及结合书本内容来完成课程设计,于是我第一想到的就是应数组来存储信息,而不是用链表,所以,对于文件数据的删除与修改,难免会过于乏力。
三.关键模块实现及主要代码
(1)自己建立的类 1.用户类: cla CUser { public: int num;char paword[20];};
2.图书信息类:
cla Book
//图书类 { public:
char author[10];
//设置作者姓名
char bname[20];
//设置图书书名
char pub_name[20];
//设置出版社名称
int pub_date;
//设置出版年月 };
(2)登录页面,由Cdialog类派生出CMyprojectDlg类,用来处理账号注册与用户登录。1.用户注册处理响应函数:
void CMyprojectDlg::OnButton3()
//用户注册 { // TODO: Add your control notification handler code here
////////////////////////////////////////////////////////////////////////////////////// //以下代码主要还是把这条记录写入文件,注意写入的格式:文件的前四个字节是保持用户记录数,//后面依次保持用户记录注意,写用户的每个信息的顺序要与读取时的顺序一致。
UpdateData(TRUE);CUser user;int n_user=0;
//用户的个数,管理员默认为第一个。自己输入为账号:1111,密码:0000。
int size=0;
fstream file(“user.dat”,ios::in|ios::out);// 建立一个文件类,关于其用法,请baidu。
file.seekg(ios::beg);
//将文件指针指向文件头。
file.read((char*)&n_user,sizeof(n_user));
//读取用户数。
n_user+=1;file.seekp(ios::beg);file.write((char*)&n_user,sizeof(n_user));//写学生记录个数
user.num=m_num;
//之前定义user.paword为string,导致密码匹配时有错误。。。
memcpy(user.paword,m_paword.GetBuffer(m_paword.GetLength()),m_paword.GetLength());// 把类CString对象的内容赋值到字符数组中
user.paword[m_paword.GetLength()]=' ';//在数组末尾加结束符号
//在文件末尾,把新的记录写入。
file.seekp(4+(n_user-1)*sizeof(CUser),ios::beg);
//这里出错
file.write((char*)&user.num,sizeof(user.num));file.write((char*)&user.paword,sizeof(user.paword));
file.close();
///////////////////////////////////////////////////////////////////////////////////////// //用消息框提示
CString str;str.Format(“成功注册,成为第 %d 个注册用户,请按确定返回重新登录.”,n_user-1);
///因为第一个为管理员,不算为用户人数。
AfxMeageBox(str);
} 2.用户登录处理响应函数。其中包含判断是管理员,还是普通用户。void CMyprojectDlg::OnButton2()
//用户登录 { // TODO: Add your control notification handler code here
UpdateData(TRUE);
CUser m_user[100];int i;int size;int n_user=0;//文件中用户的个数
////////////////////////////////////////////////////////////////////////////////////////// //读取文件中的用户记录,注意读取顺序与写入的顺序要一致,文件最开始的4个字节是表示记录个数。
//根据记录个数,依次读取用户记录 放在成员变量m_st数组中(已在类体中声明了)
CFile file;
file.Open(“user.dat”,CFile::modeReadWrite);size=GetFileSize((void*)file.m_hFile,NULL);//读取文件大小,size==0表示是空文件
if(size>0){
file.SeekToBegin();
file.Read(&n_user,sizeof(n_user));}
if(n_user>0 && n_user
{
for(i=0;i
{
//请注意与写入数据的顺序要保持一样,否则会读错数据。
file.Read(&(m_user[i].num),sizeof(m_user[i].num));
file.Read(&(m_user[i].paword),sizeof(m_user[i].paword));
} } file.Close();///////////////////////////////////////////////// if(m_num==1111&&m_paword==“0000”)
//管理员进入管理员页面
{
CAdministrators administrators;
administrators.DoModal();}
CString str_paword;for(i=0;i
//普通用户,进入图书查询页面
{
str_paword=m_user[i].paword;
if(m_user[i].num==m_num)
{
if(str_paword==m_paword)
{
CSearch search;
search.DoModal();
}
else
{
CString str;
str.Format(“密码或者账号错误,请重新输入!”);
AfxMeageBox(str);
}
} } }
(3)图书查询页面。包含读取所有图书数据,以及根据作者姓名或者书名来查询图书。1.读取所有图书响应函数。
void CSearch::OnButton5()
//显示出所有图书 { // TODO: Add your control notification handler code here int i;int size;int n_book=0;//文件book的本数
////////////////////////////////////////////////////////////////////////////////////////// //读取文件中的书本记录,注意读取顺序与写入的顺序要一致,文件最开始的4个字节是表示记录个数。
//根据记录个数,依次读取书籍记录 放在成员变量m_user_book数组中(已在类体中声明了)
CFile file;
file.Open(“book.dat”,CFile::modeReadWrite);size=GetFileSize((void*)file.m_hFile,NULL);//读取文件大小,size==0表示是空文件
if(size>0){
file.SeekToBegin();
file.Read(&n_book,sizeof(n_book));}
if(n_book>0 && n_book
{
m_num_book=n_book;
for(i=0;i
{
//请注意与写入数据的顺序要保持一样,否则会读错数据。
file.Read(&(m_user_book[i].author),sizeof(m_user_book[i].author));
file.Read(&(m_user_book[i].bname),sizeof(m_user_book[i].bname));
file.Read(&(m_user_book[i].pub_name),sizeof(m_user_book[i].pub_name));
file.Read(&(m_user_book[i].pub_date),sizeof(m_user_book[i].pub_date));
} } file.Close();///////////////////////////////////////////////// //在列表控件中显示所有条记录
//插入每列的表头;
m_list.InsertColumn(0,“作者”,LVCFMT_LEFT,100,1);
m_list.InsertColumn(1,“书名”,LVCFMT_LEFT,100,1);m_list.InsertColumn(2,“出版社”,LVCFMT_LEFT,100,1);m_list.InsertColumn(3,“出版日期”,LVCFMT_LEFT,100,1);//每一行插入一条书本记录
for(i=0;i
CString str;
str=m_user_book[i].author;
m_list.InsertItem(i,str);//InsertItem向CListCtr控件申请一行用来存放数据,同时写入一行的头名称(即编号为0列的数据)
str=m_user_book[i].bname;
m_list.SetItemText(i,1,str);//设置第i行第1列的的数据为书名
str=m_user_book[i].pub_name;
m_list.SetItemText(i,2,str);//设置第i行第2列的的数据为出版社
str.Format(“%d”,m_user_book[i].pub_date);
///整型变为字符串,便于在控件显示
m_list.SetItemText(i,3,str);//设置第i行第3列的的数据为出版日期
} }
2.图书查询响应函数。
void CSearch::OnButton4()
//查询图书 { // TODO: Add your control notification handler code here
UpdateData(TRUE);m_list.DeleteAllItems();int num=0;//表示查找结果的个数
int i;
for(i=0;i
{
CString str;
int ret1=0,ret2=0;
str=m_user_book[i].author;
ret1=str.Find(m_author,0);//ret1>=0, 表示str中包含了m_author的字段
str=m_user_book[i].bname;
ret2=str.Find(m_bname,0);//ret2>=0, 表示str中包含了m_bname的字段
if(ret1>=0)
//按作者查询
{
str=m_user_book[i].author;
m_list.InsertItem(num,str);//InsertItem向CListCtr控件申请一行用来存放数据,同时写入一行的头名称(即编号为0列的数据)
str=m_user_book[i].bname;
m_list.SetItemText(num,1,str);//设置第i行第1列的的数据为书名
str=m_user_book[i].pub_name;
m_list.SetItemText(num,2,str);//设置第i行第2列的的数据为出版社
str.Format(“%d”,m_user_book[i].pub_date);
///整型变为字符串,便于在控件显示
m_list.SetItemText(num,3,str);//设置第i行第3列的的数据为出版日期
num+=1;//结果+1;
}
} }
(4)管理员图书管理页面。包含读取所有图书,添加图书,修改图书,删除图书的功能。1.读取所有图书功能实现与(3)的实现一样。2.添加图书响应函数。
void CBookdata::OnButton7()
//添加图书 { UpdateData(TRUE);Book book;int n_book=0;
//书本的个数
int size=0;
fstream file(“book.dat”,ios::in|ios::out);// 建立一个文件类,关于其用法,请baidu。
file.seekg(ios::beg);
//将文件指针指向文件头。
file.read((char*)&n_book,sizeof(n_book));
//读取书籍数。
////////////////////////////////////////////////////////////////////////////////////// //以下代码主要还是把这条记录写入文件,注意写入的格式:文件的前四个字节是保持书本记录数,//后面依次保持书本记录注意,写学生的每个信息的顺序要与读取时的顺序一致。
n_book+=1;file.seekp(ios::beg);file.write((char*)&n_book,sizeof(n_book));//写学生记录个数
memcpy(book.author,m_add_author.GetBuffer(m_add_author.GetLength()),m_add_author.GetLength());// 把类CString对象的内容赋值到字符数组中 book.author[m_add_author.GetLength()]=' ';//在数组末尾加结束符号
memcpy(book.bname,m_add_bname.GetBuffer(m_add_bname.GetLength()),m_add_bname.GetLength());// 把类CString对象的内容赋值到字符数组中
book.bname[m_add_bname.GetLength()]=' ';//在数组末尾加结束符号
memcpy(book.pub_name,m_add_pub_name.GetBuffer(m_add_pub_name.GetLength()),m_add_pub_name.GetLength());// 把类CString对象的内容赋值到字符数组中
book.pub_name[m_add_pub_name.GetLength()]=' ';//在数组末尾加结束符号
book.pub_date=m_add_pub_date;
//在文件末尾,把新的记录写入。
file.seekp(4+(n_book-1)*54,ios::beg);
file.write((char*)&book.author,sizeof(book.author));file.write((char*)&book.bname,sizeof(book.bname));file.write((char*)&book.pub_name,sizeof(book.pub_name));file.write((char*)&book.pub_date,sizeof(book.pub_date));
file.close();///////////////////////////////////////////////////////////////////////////////////////// //用消息框提示
CString str;str.Format(“文件中已有 %d 本书籍记录.”,n_book);AfxMeageBox(str);}
3.修改图书响应函数。
void CBookdata::OnButton5()
//修改图书,,必须先读取图书才能修改图书。{ // TODO: Add your control notification handler code here UpdateData(TRUE);m_list.DeleteAllItems();m_list.InsertColumn(0,“作者”,LVCFMT_LEFT,100,1);
m_list.InsertColumn(1,“书名”,LVCFMT_LEFT,100,1);m_list.InsertColumn(2,“出版社”,LVCFMT_LEFT,100,1);m_list.InsertColumn(3,“出版日期”,LVCFMT_LEFT,100,1);
CString str_author,str_bname,str_pub_name,str;
//将book类的字符串数组赋值为字符串变量,方便比较。
CFile file;// 建立一个文件类,关于其用法,请baidu。
file.Open(“book.dat”,CFile::modeReadWrite);//打开文件
file.SeekToBegin();
file.Write(&m_num_book,sizeof(m_num_book));//写书本记录本数
for(int i=0;i
{
str_author=m_book[i].author;
str_bname=m_book[i].bname;
str_pub_name=m_book[i].pub_name;
if(str_author==m_author||str_bname==m_bname||str_pub_name==m_pub_name||m_book[i].pub_date==m_pub_date)
{
memcpy(m_book[i].author,m_modify_author.GetBuffer(m_modify_author.GetLength()),m_modify_author.GetLength());// 把类CString对象的内容赋值到字符数组中
m_book[i].author[m_modify_author.GetLength()]=' ';//在数组末尾加结束符号
memcpy(m_book[i].bname,m_modify_bname.GetBuffer(m_modify_bname.GetLength()),m_modify_bname.GetLength());// 把类CString对象的内容赋值到字符数组中
m_book[i].bname[m_modify_bname.GetLength()]=' ';//在数组末尾加结束符号
memcpy(m_book[i].pub_name,m_modify_pub_name.GetBuffer(m_modify_pub_name.GetLength()),m_modify_pub_name.GetLength());// 把类CString对象的内容赋值到字符数组中
m_book[i].pub_name[m_modify_pub_name.GetLength()]=' ';//在数组末尾加结束符号
m_book[i].pub_date=m_modify_pub_date;
}
////////////////////////////////////////////////////////////////////////////////////// //以下代码主要还是把这条记录写入文件,注意写入的格式:文件的前四个字节是保持学生记录数,//后面依次保持学生记录注意,写学生的每个信息的顺序要与读取时的顺序一致。
file.Write(&m_book[i].author,sizeof(m_book[i].author));
file.Write(&m_book[i].bname,sizeof(m_book[i].bname));
file.Write(&m_book[i].pub_name,sizeof(m_book[i].pub_name));
file.Write(&m_book[i].pub_date,sizeof(m_book[i].pub_date));
///////////////////////////////////////////////////////////////////////////////////////// //在列表控件中显示所有条记录
str=m_book[i].author;
m_list.InsertItem(i,str);//InsertItem向CListCtr控件申请一行用来存放数据,同时写入一行的头名称(即编号为0列的数据)
str=m_book[i].bname;
m_list.SetItemText(i,1,str);//设置第i行第1列的的数据为书名
str=m_book[i].pub_name;
m_list.SetItemText(i,2,str);//设置第i行第2列的的数据为出版社
str.Format(“%d”,m_book[i].pub_date);
///整型变为字符串,便于在控件显示
m_list.SetItemText(i,3,str);//设置第i行第3列的的数据为出版日期
}
file.Close();
//用消息框提示
str.Format(“成功修改书籍记录.”);AfxMeageBox(str);}
4.删除图书响应函数。
void CBookdata::OnButton6()
//删除图书,,读取了图书数据后才能删除图书 { // TODO: Add your control notification handler code here UpdateData(TRUE);m_list.DeleteAllItems();m_list.InsertColumn(0,“作者”,LVCFMT_LEFT,100,1);
m_list.InsertColumn(1,“书名”,LVCFMT_LEFT,100,1);m_list.InsertColumn(2,“出版社”,LVCFMT_LEFT,100,1);m_list.InsertColumn(3,“出版日期”,LVCFMT_LEFT,100,1);
CString str_author,str_bname,str_pub_name,str;
//将book类的字符串数组赋值为字符串变量,方便比较。
int n_book,n_flag=0;
// n_flag为删除标志,1为有元素删除了
CFile file;// 建立一个文件类,关于其用法,请baidu。
file.Open(“book.dat”,CFile::modeReadWrite);//新建一个文件并且打开文件
file.SeekToBegin();
file.Write(&m_num_book,sizeof(m_num_book));//写书本记录本数,先填入本来的书本数。
for(int i=0;i
{
str_author=m_book[i].author;
str_bname=m_book[i].bname;
str_pub_name=m_book[i].pub_name;
if(str_author==m_del_author||str_bname==m_del_bname||str_pub_name==m_del_pub_name||m_book[i].pub_date==m_del_pub_date)
{
n_flag=1;
//删除标志置1
i+=1;
//第i个元素为要删除元素,跳过
}
////////////////////////////////////////////////////////////////////////////////////// //以下代码主要还是把这条记录写入文件,注意写入的格式:文件的前四个字节是保持学生记录数,//后面依次保持学生记录注意,写学生的每个信息的顺序要与读取时的顺序一致。
file.Write(&m_book[i].author,sizeof(m_book[i].author));
file.Write(&m_book[i].bname,sizeof(m_book[i].bname));
file.Write(&m_book[i].pub_name,sizeof(m_book[i].pub_name));
file.Write(&m_book[i].pub_date,sizeof(m_book[i].pub_date));
}
if(n_flag==1)
{
n_book=m_num_book-1;
//表示删除了一本书,则将文件的书本数减1。
file.SeekToBegin();
file.Write(&n_book,sizeof(n_book));//写书本记录本数,先填入本来的书本数。
//用消息框提示
str.Format(“成功删除书籍记录.”);
AfxMeageBox(str);
}
else
{
//用消息框提示
str.Format(“没有这一本书的记录,不能删除.”);
AfxMeageBox(str);
}
file.Close();}
(5)、用户信息管理页面,包含读取所有用户信息,删除、添加、修改用户信息。1.读取所有用户信息以(4)基本相同。
2.删除用户信息实现基本方法与(4)基本相同。3.添加用户信息实现基本方法与(4)基本相同。4.修改用户信息实现基本方法与(4)基本相同。
四.实验结果及分析
(1.)登录页面成功注册
普通用户成功登录页面
查询结果
管理员登录页面
图书管理页面
成功添加图书。
用户信息管理页面
分析:基本功能是可以实现的,但是因为是用数组来存放图书信息与用户信息,所以对于数据大了之后处理能力不够方便以及效率不高。而且删除信息的时候只是删除该条数据的信息,数据后面的信息往上移动,所以最后面会有两条重复的信息,虽然文件开头储存信息数目已经减一,读取数据是按照文件开头储存的信息数目来读取信息,但是有时候读取会有乱码。我在程序中找不到那个隐藏的bug,五、心得体会
这次C++课程设计学到了很多,懂得了C++看起来虽然简单,可是要用这个计算机语言来实现一些有用的功能,怎么运用确实是一门挺高深的学问,我们学习计算机语言不能只是看书看概念,要多上机实验,这样才能对计算机语言有比较深的了解,才能够提高自己的编程以及调试找BUG的能力。现在来总结一下编程过程中遇到的一些值得注意的问题。
1.派生出一个类时,为编辑框增加变量名后,然后在类声明中自己增加类成员,如果再想在对话框中增加编辑框并且添上变量名会出现错误。(如下,在Csearch 类中自己添加下面箭头指向的代码后,会出现下图右的错误,解决方法是把箭头指向的代码放到//}}AFX_DATA后面,因为此语句为MFC对话框自行添加编辑变量的结束,如果我们将自己定义的变量也添加到此语句前,编译器不清楚是MFC自行添加的变量还是用户自行添加的变量。)2.遇到编译器没有出错,可是运行结果与自己想象的不一样,很可能是逻辑错误。如: 在用户注册时,我是将用户数据直接存到文件尾,这是不正确的,因为管理员页面有删除用户的功能,这个功能的原理是先读文件头的用户数(假定为10个),然后将用户的数据先读取出来,然后经过一个判断,再储存重新储存回文件,而文件中储存的用户数据大小是没有变化的,也就是说文件尾会有两份最后一个用户的数据(即如果删除的是第七个,文件中最后会有两份第十个用户的数据,文件头的记录用户数为9个),如果添加用户直接储存在文件尾则会储存在第11个用户的位置,可是以后读取的话只是读取到第10个。所以会出现读取错误,密码不匹配的问题。应该根据记录的用户数让文件指针指向最后一个用户数据后面。file.seekp(4+(n_user-1)*sizeof(CUser),ios::beg);
以上两个问题是困扰我比较久的,其他问题经过百度还有自己的调试,解决还算快,不值得重点说。
六、参考文献
1.C++面向对象程序设计(第二版)
谭浩强 编著 2.成绩管理系统MFC教程
3.CNDN论坛,关于文件操作的指针运算。