#include "StuList.h"
#include"BSTree.h"
#include <string>
#include <sstream> // 添加这个头文件以使用 std::istringstream
#include <limits> // 包含limits头文件以使用std::numeric_limits
void op_user_menu(); //用户操作菜单函数
void Register(SqList &L); //用户注册函数,输入用户信息X,插入到表L
void Log_in(SqList L); //用户登录函数,输入学号和密码进行登录验证
void Log_out(SqList &L);//用户注销函数,输入学号,在顺序表中删除该用户
void Modify_Password(SqList &L);//用户密码修改函数,输入学号,修改该用户的登录密码
void File_in_List(SqList &L); //导入数据函数:打开student.txt文件,导入用户信息到顺序表L
void List_out_file(SqList L);//导出数据函数:将顺序表L中的数据导出到student.txt保存
void PrintList(SqList L);//用户信息显示函数:按行显示顺序表L中的用户信息。
void Borrow_Book(SqList &students, BSTree &books, string studentNum);
int main() {
op_user_menu();
}
void op_user_menu() //用户操作菜单函数
{
int op;
SqList L; // 定义单链表变量L
File_in_List(L); // 导入用户数据到表L
do {
cout << endl;
cout << "/***********************输入数字进行操作选择************************/" << endl;
cout << "1.注册" << endl;
cout << "2.登录" << endl;
cout << "3.输入学号修改登录密码" << endl;
cout << "4.输入学号进行账号注销" << endl;
cout << "5.显示所有学生信息" << endl;
cout << "0.保存并退出程序" << endl;
cout << "请输入: ";
// 检查输入是否为数字
if (!(cin >> op)) {
// 如果输入不是数字,清除输入流的错误标志
cin.clear();
// 忽略错误输入后的所有字符
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "输入无效,请重新输入: ";
continue; // 继续循环,等待用户重新输入
}
// 检查输入是否在1到5之间
if (op < 1&&op!=0 || op > 5 ) {
cout << "输入无效,请输入1到5之间的数字: ";
continue; // 继续循环,等待用户重新输入
}
// 根据用户输入执行相应操作
switch (op) {
case 1:
Register(L);
break;
case 2:
Log_in(L);
break;
case 3:
Modify_Password(L);
break;
case 4:
Log_out(L);
break;
case 5:
PrintList(L);
break;
case 0:
cout << "数据已保存,程序退出。" << endl;
return; // 退出函数,结束程序
default:
// 如果输入的数字不在1到5之间,但已经通过了之前的检查,这里不应该执行
break;
}
} while (true); // 无限循环,直到用户选择退出
}
// 用户注册函数
void Register(SqList &L) {
student newStudent;
cout << "输入学号:" << endl;
cin >> newStudent.num;
cout << "输入姓名:" << endl;
cin >> newStudent.name;
cout << "输入专业:" << endl;
cin >> newStudent.major;
cout << "输入密码:" << endl;
cin >> newStudent.password;
// 插入到顺序表
int i = L.length + 1; // 新元素插入的位置
if (ListInsert_Sq(L, i, newStudent) == OK) {
cout << "注册成功!" << endl;
// 将新插入的学生信息写入到student.txt文件中
fstream file("student.txt", ios::app); // 打开文件用于追加内容
if (file.is_open()) {
try {
file << newStudent.num << "\t" << newStudent.name << "\t" << newStudent.major << "\t" << newStudent.password << endl;
file.close(); // 关闭文件
cout << "学生信息录入成功!" << endl; // 添加成功提示信息
} catch (const exception& e) {
cerr << "写入文件时发生错误: " << e.what() << endl;
}
} else {
cerr << "无法打开文件以写入数据!" << endl;
}
} else {
cout << "注册失败,可能是因为顺序表已满。" << endl;
}
}
// 用户登录函数
void Log_in(SqList L)
{
BSTree T;
int j;
string login_user, login_pass;
cout << "请输入学号和密码进行登录" << endl;
cout << "学号:" << endl;
cin >> login_user;
cout << "密码:" << endl;
cin >> login_pass;
// 根据学号进行查找
j = LocateElem_Sq(L, login_user);
if (j && L.elem[j].password == login_pass)
{
cout << "登录成功" << endl;
// 登录成功后,将学号存储在变量中
string studentNum = login_user;
// 进入借书菜单
int op;
do {
cout << endl;
cout << "/***********************输入数字进行操作选择************************/" << endl;
cout << "1.借书" << endl;
cout << "2.还书" << endl;
cout << "0.保存并退出程序" << endl;
cout << "请输入: ";
// 检查输入是否为数字
if (!(cin >> op)) {
// 如果输入不是数字,清除输入流的错误标志
cin.clear();
// 忽略错误输入后的所有字符
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "输入无效,请重新输入: ";
continue; // 继续循环,等待用户重新输入
}
// 检查输入是否在1到3之间
if (op < 1 && op != 0 || op > 2) {
cout << "输入无效,请输入1到2之间的数字: ";
continue; // 继续循环,等待用户重新输入
}
// 根据用户输入执行相应操作
switch (op) {
case 1:
// 创建一个BSTree类型的变量
// 初始化BSTree
BSTree books;
// 使用已存储的学号调用借书函数
Borrow_Book(L, books, studentNum);
break;
case 2:
// 还书函数
// Return_Book(L, T);
break;
case 0:
cout << "数据已保存,程序退出。" << endl;
return; // 退出函数,结束程序
default:
// 如果输入的数字不在1到5之间,但已经通过了之前的检查,这里不应该执行
break;
}
} while (true); // 无限循环,直到用户选择退出
} else {
cout << "账号密码错误,重新输入" << endl;
}
}
// 借书功能实现
// 借书功能实现
void Borrow_Book(SqList &students, BSTree &books, string studentNum) {
string bookId;
int studentIndex; // 声明studentIndex变量
BSTNode *bookNode; // 声明bookNode变量
cout << "请输入要借阅的图书ISBN号: ";
cin >> bookId;
// 查找图书
bookNode = SearchBST(books, bookId); // 声明并初始化bookNode变量
if (bookNode == NULL) {
cout << "图书不存在。" << endl;
return;
}
// 检查库存
if (bookNode->data.num <= 0) {
cout << "图书已借完。" << endl;
return;
}
// 更新库存并记录借阅
bookNode->data.num--;
studentIndex = LocateElem_Sq(students, studentNum); // 假设LocateElem_Sq函数返回学生在顺序表中的索引
if (studentIndex != -1) {
students.elem[studentIndex].borrowedBooks.push_back(bookNode->data);
cout << "借书成功!" << endl;
// 将借书信息写入到stu_borrow.txt文件中
ofstream outFile("stu_borrow.txt", ios::app); // 打开文件用于追加内容
if (outFile.is_open()) {
outFile << studentNum << "\t" << bookId << endl;
outFile.close();
} else {
cerr << "无法打开文件以写入数据!" << endl;
}
} else {
cout << "未找到该学生信息。" << endl;
}
}
// 用户注销函数
void Log_out(SqList &L)
{
int j;
string login_user1,login_user2;
cout<<"输入学号进行注销"<<endl;
cout<<"学号:"<<endl;
cin>>login_user1;
cout<<"再次输入学号确认"<<endl;
cin>>login_user2;
while (login_user1!=login_user2 )
{
cout<<"账号输入错误,重新输入"<<endl;
cout<<"输入学号进行注销"<<endl;
cout<<"学号:"<<endl;
cin>>login_user1;
cout<<"再次输入学号确认"<<endl;
cin>>login_user2;
}
j=LocateElem_Sq(L,login_user1);
if (j!=0)
{
ListDelete_Sq(L,j);
cout<<"注销账号成功"<<endl;
}
else cout<<"输入账号不存在"<<endl;
return ;
}
// 用户信息修改函数
void Modify_Password(SqList &L)
{
int j;
string login_user,pass1,pass2;
cout<<"输入学号进行信息修改"<<endl;
cout<<"学号:"<<endl;
cin>>login_user;
j=LocateElem_Sq(L,login_user);
if (j!=0)
{
cout<<"新密码:"<<endl;
cin>>pass1;
cout<<"再次输入新密码:"<<endl;
cin>>pass2;
while (pass1!=pass2)
{
cout<<"两次密码不一致,重新输入"<<endl<<endl;
cout<<"新密码:"<<endl;
cin>>pass1;
cout<<"再次输入新密码:"<<endl;
cin>>pass2;
}
L.elem[j].password=pass1;
cout<<"密码修改成功"<<endl;
}
else
cout<<"该用户不存在"<<endl;
return ;
}
void File_in_List(SqList &L) //将文件中的数据写入到L
{
ElemType1 e;
fstream file;
InitList_Sq(L);
int i = 1;
file.open("student.txt");
if (!file) {
cout << "错误!未找到文件!" << endl;
exit(ERROR);
}
while (file >> e.num >> e.name >> e.major >> e.password) {
ListInsert_Sq(L, i, e);
i++;
}
cout << "输入 student.txt 信息完毕\n\n";
file.close();
}
void List_out_file(SqList L) //表L的数据写回文件book.txt
{
int i;
student e;
fstream file("student.txt", fstream::out | ios_base::trunc); //打开时清空文件
if (!file) {
cout << "错误!未找到文件!" << endl;
exit(ERROR);
}
for (i=1;i<L.length;i++)
{
e=L.elem[i];
file << left << e.num<< "\t" << left << e.name << "\t" << left << e.major << "\t" << left << e.password <<endl;
}
e=L.elem[L.length];
file << left << e.num<< "\t" << left << e.name << "\t" << left << e.major << "\t" << left << e.password;
file.close();
}
//按行输出用户的学号、姓名、专业
void PrintList(SqList L)
{
int i;
for(i=1;i<=L.length;i++)
cout<<L.elem[i].num<<" "<<L.elem[i].name<<" "<<L.elem[i].major<<endl;
cout<<endl;
}
//
//StuList.h文件包含了顺序表结构的类型描述、基本操作的实现
//
#include"BSTree.h"
#include<iostream>
#include<string>
#include<fstream>
#include <vector>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100 //顺序表可能达到的最大长度
/*程序中相关数据类型定义*/
typedef int Status; //Status 是函数返回值类型,其值是函数结果状态代码。
typedef struct {
string num;//学号
string name;//姓名
string major;//专业
string password;//登录密码
vector<Book> borrowedBooks; // 存储学生借阅的书籍信息
} student; //定义用户信息类型student
typedef student ElemType1; //顺序表中的数据元素类型定义为student
typedef struct {
ElemType1 * elem; //存储空间的基地址,从下标1开始写数据
int length; //当前长度
} SqList; //定义顺序表类型
Status InitList_Sq(SqList &L) { //算法2.1 顺序表的初始化
//构造一个空的顺序表L
L.elem = new ElemType1[MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的数组空间
if (!L.elem)
exit(OVERFLOW); //存储分配失败退出
L.length = 0; //空表长度为0
return OK;
}
Status GetElem(SqList L, int i, ElemType1 &e) {//算法2.2 顺序表的取值
if (i < 1 || i > L.length)
return ERROR; //判断i值是否合理,若不合理,返回ERROR
e = L.elem[i]; //elem[i]单元存储第i个数据元素
return OK;
}
int LocateElem_Sq(SqList L, string stu_num) { //根据学号进行查找,查找成功返回位序
for (int i = 1; i <=L.length; i++)
if (L.elem[i].num == stu_num)
return i ;//查找成功,返回位序
return -1;//查找失败,返回0
}
Status ListInsert_Sq(SqList &L, int i, ElemType1 e) { //算法2.4 顺序表的插入
//在顺序表L中第i个位置之前插入新的元素e
//i值的合法范围是1<=i<=L.length+1
if ((i < 1) || (i > L.length + 1))
return ERROR; //i值不合法
if (L.length == MAXSIZE)
return ERROR; //当前存储空间已满
for (int j = L.length; j >= i; j--)
L.elem[j + 1] = L.elem[j]; //插入位置及之后的元素后移
L.elem[i] = e; //将新元素e放入第i个位置
++L.length; //表长增1
return OK;
}
Status ListDelete_Sq(SqList &L, int i) { //算法2.5 顺序表的删除
//在顺序表L中删除第i个元素,并用e返回其值
//i值的合法范围是1<=i<=L.length
if ((i < 1) || (i > L.length))
return ERROR; //i值不合法
for (int j = i; j < L.length; j++)
L.elem[j] = L.elem[j+1]; //被删除元素之后的元素前移
--L.length; //表长减1
return OK;
}
//算法7.4 二叉排序树的递归查找
//算法7.5 二叉排序树的插入
//算法7.6 二叉排序树的创建
//算法 7.7 二叉排序树的删除
#ifndef BSTREE_H
#define BSTREE_H
#include<iostream>
#include<fstream>
#include <string>
#include <iomanip>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define MAXSIZE 100
typedef struct {
string id; //ISBN
string name; //书名
double price; //定价
int num; //库存
} Book; //定义图书信息数据类型
typedef string Keytype; //定义查找关键字类型Keytype为string类型
typedef Book ElemType2;
typedef struct BSTNode{
ElemType2 data; //结点数据域,存数据元素
BSTNode *lchild,*rchild; //左右孩子指针
} BSTNode,* BSTree; //二叉排序树结点及二叉排序树类型定义
// 定义BSTree类型
//算法7.4 二叉排序树的递归查找
BSTree SearchBST(BSTree T,Keytype key) { //根据ISBN号查找
//在根指针T所指二叉排序树中递归地查找某关键字等于key的数据元素
//若查找成功,则返回指向该数据元素结点的指针,否则返回空指针
if((!T)|| key==T->data.id) return T; //查找结束
else if (key<T->data.id) return SearchBST(T->lchild,key); //在左子树中继续查找
else return SearchBST(T->rchild,key); //在右子树中继续查找
} // SearchBST
//算法7.5 二叉排序树的插入
void InsertBST(BSTree &T,ElemType2 e ) {
//当二叉排序树T中不存在关键字等于e.key的数据元素时,则插入该元素
if(!T) { //找到插入位置,递归结束
BSTree S = new BSTNode; //生成新结点*S
S->data = e; //新结点*S的数据域置为e
S->lchild = S->rchild = NULL; //新结点*S作为叶子结点
T =S; //把新结点*S链接到已找到的插入位置
}
else if (e.id< T->data.id)
InsertBST(T->lchild, e ); //将*S插入左子树
else if (e.id> T->data.id)
InsertBST(T->rchild, e); //将*S插入右子树
}// InsertBST
BSTNode* FindMin( BSTree T )
{
BSTNode* p;
if(T==NULL) return NULL; //空树返回空指针
p=T;
while(p->lchild) //找最左边的
{
p=p->lchild;
}
return p;
}
//算法 7.7 二叉排序树的删除
void DeleteBST(BSTree &T, Keytype key) {
BSTNode *temp;
if (!T) return; // 空树,删除失败
if (T->data.id > key) {
DeleteBST(T->lchild, key); // 在左子树中递归删除
} else if (T->data.id < key) {
DeleteBST(T->rchild, key); // 在右子树中递归删除
} else {
// 找到要删除的节点
if (!T->lchild && !T->rchild) {
// 没有子节点,直接删除
delete T;
T = NULL;
} else if (!T->lchild) {
// 只有右子节点,用右子节点替换当前节点
BSTNode *temp = T;
T = T->rchild;
delete temp;
} else if (!T->rchild) {
// 只有左子节点,用左子节点替换当前节点
BSTNode *temp = T;
T = T->lchild;
delete temp;
} else {
// 有两个子节点,找到右子树的最小节点
temp = FindMin(T->rchild);
// 用右子树的最小节点替换当前节点
T->data = temp->data;
// 删除右子树的最小节点
DeleteBST(T->rchild, temp->data.id);
}
}
}
#endif // BSTREE_H