/****************************************************\
模块名称:
基于二叉排序树的身份证管理系统
摘要:
(1)能够进行身份证号码及相关信息的录入,相关信息包括姓名、年龄和手机号;
(2)能够快速进行身份证号码的查询,并输出相关信息;
(3)可以修改身份证号码对应的其他信息,如姓名、地址;
(4)可以完成身份证信息的删除。
(5)录入信息可以保存到数据文件中。
(6)可以将文件信息录入到管理系统中
(7)需要做到识别出身份证号码错误、超出规定长度等。
叶艺衡于2023年5月13日创建本模块
\****************************************************/
#include <iostream>
#include <fstream>
#include < sstream >
#include <map>
using namespace std;
// 注册账号
void registerAccount(map<string, string>& accounts)
{
string username, password;
cout << "请输入用户名(10位):";
cin >> username;
if (username.length() != 10) {
cout << "该用户名不符合规范,请重新输入!" << endl;
return registerAccount(accounts);
}
// 检查用户名是否已经存在
if (accounts.find(username) != accounts.end()) {
cout << "该用户名已经存在,请重新输入!" << endl;
return registerAccount(accounts);
}
cout << "请输入密码(10位):";
cin >> password;
if (password.length() != 10) {
cout << "该密码不符合规范,请重新输入!" << endl;
return registerAccount(accounts);
}
// 将账号密码存储到map中
accounts[username] = password;
cout << "注册成功!" << endl;
}
// 导出账号密码
void exportAccounts(map<string, string>& accounts)
{
ofstream outFile("accounts.txt");
// 将map中的账号密码写入文件
for (auto it = accounts.begin(); it != accounts.end(); ++it) {
outFile << it->first << " " << it->second << endl;
}
outFile.close();
}
// 读取账号密码
void readAccounts(map<string, string>& accounts)
{
ifstream inFile("accounts.txt");
// 读取文件中的账号密码,并存储到map中
string username, password;
while (inFile >> username >> password) {
accounts[username] = password;
}
inFile.close();
}
// 验证账号密码
bool verifyAccount(map<string, string>& accounts, string username, string password)
{
// 检查用户名是否存在
if (accounts.find(username) == accounts.end()) {
cout << "用户名不存在,请重新输入!" << endl;
return false;
}
// 比较输入的密码和map中存储的密码是否一致
if (accounts[username] != password) {
cout << "密码错误,请重新输入!" << endl;
return false;
}
// 验证通过
return true;
}
// 登录账号
void loginAccount(map<string, string>& accounts)
{
string username, password;
cout << "请输入用户名:";
cin >> username;
cout << "请输入密码:";
cin >> password;
if (verifyAccount(accounts, username, password)) {
cout << "登录成功!" << endl;
}
else {
loginAccount(accounts);
}
}
struct IDCard {
string id;
string name;
string age;
string time;
};
struct BSTNode {
IDCard data;
BSTNode* left;
BSTNode* right;
BSTNode* parent;
int balanceFactor;
};
/***********************************************\
函数名称:creatBST
功能描述:创建一个新的二叉排序树节点
函数参数:
data:要插入的IDCard数据
返回值:
返回一个新创建的BSTNode节点,如果创建失败则返回nullptr
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
BSTNode* creatBST(IDCard data) {// 创建一个新的二叉排序树节点
BSTNode* node = new BSTNode;
node->data = data;
node->left = nullptr;
node->right = nullptr;
return node;
}
/***********************************************\
函数名称:insertBST
功能描述:将一个IDCard插入到二叉搜索树中
函数参数:
IDCard data: 要插入的IDCard数据
BSTNode* node: 当前操作的BSTNode指针
返回值:BSTNode*: 返回操作后的BSTNode指针
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
BSTNode* insertBST(IDCard data, BSTNode* node) {
if (node == nullptr) {
node = creatBST(data);
}
else if (data.id < node->data.id) {
node->left = insertBST(data, node->left);
}
else if (data.id > node->data.id) {
node->right = insertBST(data, node->right);
}
return node;
}
/***********************************************\
函数名称:findMinNode
功能描述:在二叉搜索树中查找最小结点
函数参数:
BSTNode* node:指向当前二叉搜索树的指针,用于查找最小结点
返回值:
BSTNode*:返回查找到的最小结点的指针
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
/*查找最小结点,用于后续删除算法时如果有左右子树,
找到其右子树中的最小结点替换其值*/
BSTNode* findMinNode(BSTNode* node) {
while (node->left != nullptr) {
node = node->left;
}
return node;
}
/***********************************************\
函数名称: deleteBST
功能描述: 在二叉搜索树中删除指定的数据,并返回根节点指针
函数参数:
node: 当前递归处理的二叉搜索树节点指针
data: 待删除的数据
返回值:
返回删除操作后的二叉搜索树的根节点指针
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
BSTNode* deleteBST(BSTNode* node, IDCard data) {
if (node == nullptr) {
return nullptr;
}
//递归,查找需要删除的结点
if (data.id < node->data.id) {
node->left = deleteBST(node->left, data);
}
else if (data.id > node->data.id) {
node->right = deleteBST(node->right, data);
}
else {
// 找到要删除的结点
if (node->left == nullptr && node->right == nullptr) {
// 如果是叶子结点,直接删除
delete node;
node = nullptr;
}
else if (node->left == nullptr) {
// 如果只有右子树,将其右子树移至删除结点的位置
BSTNode* temp = node;
node = node->right;
delete temp;
}
else if (node->right == nullptr) {
// 如果只有左子树,将其左子树移至删除结点的位置
BSTNode* temp = node;
node = node->left;
delete temp;
}
else {
// 如果有左右子树,找到其右子树中的最小结点替换其值
BSTNode* minNode = findMinNode(node->right);
node->data.id = minNode->data.id;
node->right = deleteBST(node->right, minNode->data);
}
}
return node;
}
int getHeight(BSTNode* node) {
if (node == nullptr) {
return 0;
}
int leftHeight = getHeight(node->left);
int rightHeight = getHeight(node->right);
return max(leftHeight, rightHeight) + 1;
}
//计算平衡因子(-1,0,1),并将其作为结果返回
int getBalanceFactor(BSTNode* node) {
int leftHeight = getHeight(node->left);
int rightHeight = getHeight(node->right);
return rightHeight - leftHeight;
}
// 更新BST结构并更新压缩路径信息
BSTNode* searchBST(IDCard data, BSTNode* node);
void updateBST(IDCard data, BSTNode* node) {
BSTNode* p = searchBST(data, node);
if (p == nullptr) return;
// 更新节点数据
p->data = data;
// 更新左子树和右子树
BSTNode* left = p->left;
BSTNode* right = p->right;
p->left = nullptr;
p->right = nullptr;
insertBST(data, node->left);
insertBST(data, node->right);
// 更新父节点
BSTNode* parent = p->parent;
if (parent != nullptr) {
if (parent->left == p) {
parent->left = left != nullptr ? left : right;
}
else {
parent->right = left != nullptr ? left : right;
}
}
else {
node = left != nullptr ? left : right;
}
// 更新所有受影响的节点的压缩路径信息
while (parent != nullptr) {
parent->balanceFactor = getBalanceFactor(parent);
parent = parent->parent;
}
}
/***********************************************\
函数名称: whether_choose
功能描述:让用户选择是否继续使用该系统
函数参数:
choose:选择
返回值: choose
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
void isTrue(int functionality) {
switch (functionality) {
case 1: //判断是否使用
int choose;
cout << "\n继续使用:1\n结束使用:0\n请输入您的选择:";
cin >> choose;
system("cls");
if (choose == 0)
exit(0);
break;
case 2:
break;
// 添加更多功能的判断规定
default:
// 未知功能编号,返回false或抛出错误
break;
}
// 默认返回false,可以根据需要进行修改
}
/***********************************************\
函数名称: outfiletxt
功能描述: 将二叉搜索树中的 IDCard 数据输出到指定的输出流中。
导出身份证信息,并将导出的文档设置为txt格式,保存在根目录下
函数参数:
node: 当前遍历的二叉搜索树节点。
outfile: 要输出数据的输出流。
返回值: 无返回值。
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
BSTNode* searchBST(IDCard data, BSTNode* node);
void modifyBST(IDCard data, BSTNode* node) {
BSTNode* target = searchBST(data, node );
if (target== nullptr) {
cout << "身份证号码不存在" << endl;
return;
}
cout << "请输入新的姓名:" << endl;
cin >> target->data.name;
bool isValud1 = false;
while (!isValud1) {
cout << "请输入年龄:";
cin >> target->data.age;
if ((target->data.age.length() == 1 || target->data.age.length() == 2) &&
isdigit(target->data.age[0]) &&
(target->data.age.length() == 1 || isdigit(target->data.age[1])) &&
stoi(target->data.age) >= 0 && stoi(target->data.age) <= 120) {
// 年龄符合要求,执行相应操作
isValud1 = true;
}
else {
cout << "您输入的年龄有误" << endl;
}
}
bool isValid = false;
while (!isValid) {
cout << "请输入新的签发日期(xxxx.xx):" << endl;
cin >> target->data.time;
if (target->data.time.length() != 7 ||
target->data.time[4] != '.' ||
(target->data.time[5] != '0' && target->data.time[5] != '1') ||
(target->data.time[5] == '1' && (target->data.time[6] < '0' || target->data.time[6] > '2')) ||
(target->data.time[5] == '0' && (target->data.time[6] < '0'))
) {
cout << "输入的签发日期格式错误" << endl;
}
else {
isValid = true;
}
}
cout << "修改成功!" << endl;
}
void inorderTraversal(BSTNode* node) {
if (node != nullptr) {
inorderTraversal(node->left);
cout << node->data.id<<'\t';
cout <<node->data.name << '\t';
cout << node->data.age << '\t';
cout << node->data.time << '\t';
cout << endl;
inorderTraversal(node->right);
}
}
/***********************************************\
函数名称: outfiletxt
功能描述: 将二叉搜索树中的 IDCard 数据输出到指定的输出流中。
导出身份证信息,并将导出的文档设置为txt格式,保存在根目录下
函数参数:
node: 当前遍历的二叉搜索树节点。
outfile: 要输出数据的输出流。
返回值: 无返回值。
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
//将二叉搜索树中每个节点的数据写入到文件中
void outfiletxt(BSTNode* node, ofstream& outfile) {
//如果当前节点非空
if (node != nullptr) {
//遍历左子树
outfiletxt(node->left, outfile);
//将当前节点的数据写入文件
outfile << "姓名:" << node->data.name << " 身份证:" << node->data.id << " 年龄:" << node->data.age<< " 签发日期:" << node->data.time << endl;
//遍历右子树
outfiletxt(node->right, outfile);
}
}
/***********************************************\
函数名称: infiletxt
功能描述: 从指定文件中读取 IDCard 数据,将其插入到二叉搜索树中。
函数参数:
node: 二叉搜索树的根节点指针的引用。
filename: 要读取数据的文件的文件名。
返回值: 无返回值。
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
void infiletxt(BSTNode*& node, const string& filename) {
//打开指定文件
ifstream infile(filename);
//如果文件打开失败
if (!infile.is_open()) {
//输出错误信息并返回
cout << "文件打开失败" << endl;
return;
}
//逐行读取文件
string line;
while (getline(infile, line)) {
//使用 stringstream 将一行数据分解为三个字段
stringstream ss(line);
string id, name, age, time;
//在每行中逐句输入身份证号,姓名,年龄,并以逗号作为分隔符
getline(ss, id, ',');
getline(ss, name, ',');
getline(ss, age, ',');
getline(ss, time, ',');
//创建一个 IDCard 结构体对象
IDCard data = { id, name, age,time };
if (data.id.length() != 18 || data.time.length() != 7 || data.time[4] != '.') {
cout << "(文件中出现格式错误信息,导入中断,请检查......)" << endl;
return;
}
if (searchBST(data, node) != nullptr) {
cerr << "导入的身份证内容出现重复,导入中断,请检查......";
return;
}
//将该对象插入到二叉搜索树中
node = insertBST(data, node);
}
cout << "导入成功!" << endl;
//关闭文件
infile.close();
}
/***********************************************\
函数名称: searchBST
功能描述: 在二叉搜索树中查找与给定IDCard数据相匹配的节点。
函数参数:
data: 要查找的IDCard数据。
node: 当前查找的节点。
返回值:
如果找到匹配的节点,则返回该节点。
如果未找到匹配的节点,则返回空指针。
模块:
创建人: 叶艺衡,时间: 2023.5.13
修改人: 叶艺衡,时间: 2023.5.13
\**********************************************/
BSTNode* searchBST(IDCard data, BSTNode* node) {
if (node == nullptr || node->data.id == data.id) {
return node;
}
else if (data.id > node->data.id) {
return searchBST(data, node->right);
}
else if (data.id < node->data.id) {
return searchBST(data, node->left);
}
return nullptr;
}
/***********************************************\
函数名称:
main
功能描述:
通过主页面上的数字来选择对应的功能
模块 :
叶艺衡于2023年5月13日创建本模块
叶艺衡于2023年5月13日修改本模块
修改原因:增加可读性
\**********************************************/
int main() {
int choice;
BSTNode* node = nullptr;
IDCard data;
int choose = 1;
map<string, string> accounts;
// 读取之前导出的账号密码
readAccounts(accounts);
while (true) {
cout << "\n";
cout << " ------------------身份证管理系统登录页面-----------------" << endl;
cout << " ======================================================" << endl;
cout << " = 1- 注册用户 =" << endl;
cout << " = 2- 登录账号 =" << endl;
cout << " = 3- 退出 =" << endl;
cout << " ======================================================" << endl;
cout << " \n请输入你的选择:" << endl;
cin >> choice;
switch (choice) {
case 1:
registerAccount(accounts);
exportAccounts(accounts);
break;
case 2:
loginAccount(accounts);
system("cls");
cout << "\n";
cout << " ------------------欢迎来到身份证管理系统-----------------" << endl;
while (choose == 1) {
cout << " " << endl;
cout << " 主页 " << endl;
cout << " ======================================================" << endl;
cout << " = 1-输入身份证信息 =" << endl;
cout << " = 2-删除身份证信息 =" << endl;
cout << " = 3-查找身份证信息 =" << endl;
cout << " = 4-修改身份证信息 =" << endl;
cout << " = 5-展示身份证信息 =" << endl;
cout << " = 6-导出身份证信息 =" << endl;
cout << " = 7-导入身份证信息 =" << endl;
cout << " = 8-退出系统 =" << endl;
cout << " ======================================================" << endl;
cout << "@请输入你的选择:";
int choice;
cin >> choice;
system("cls");
IDCard data;
BSTNode* search;
bool isValid = false;
bool isValud1 = false;
switch (choice) {
case 1:
cout << " ------------------输入身份证信息---------------------" << endl;
cout << "请输入身份证号码:";
cin >> data.id;
if (searchBST(data, node) == nullptr) {
if (data.id.length() != 18) {
cout << "格式错误\n";
isTrue(1);
break;
}
cout << "请输入姓名:";
cin >> data.name;
while (!isValud1) {
cout << "请输入年龄:";
cin >> data.age;
if ((data.age.length() == 1 || data.age.length() == 2) &&
isdigit(data.age[0]) &&
(data.age.length() == 1 || isdigit(data.age[1])) &&
stoi(data.age) >= 0 && stoi(data.age) <= 100)
{
isValud1 = true;
}
else {
cout << "您输入的年龄有误" << endl;
}
}
while (!isValid) {
cout << "请输入签发日期(xxxx.xx):";
cin >> data.time;
if (data.time.length() != 7 ||
data.time[4] != '.' ||
(data.time[5] != '0' && data.time[5] != '1' ) ||
(data.time[5] == '1' && (data.time[6] < '0' || data.time[6] > '2'))||
(data.time[5] == '0' && (data.time[6] <= '0' ))
)
{
cout << "输入的签发日期格式错误" << endl;
}
else {
isValid = true;
}
}
node = insertBST(data, node);
cout << "身份证号码存储成功!\n";
isTrue(1);
}
else {
cout << "身份证号码已存在" << endl;
isTrue(1);
}
break;
case 2:
cout << " ------------------删除身份证信息---------------------" << endl;
cout << "请输入要删除的身份证号码:";
cin >> data.id;
if (data.id.length() != 18) {
cout << "格式错误\n";
isTrue(1);
break;
}
search = searchBST(data, node);
if (search == nullptr) {
cout << "身份证号码不存在\n";
isTrue(1);
break;
}
node = deleteBST(node, data);
cout << "删除成功";
updateBST(data, node);
isTrue(1);
break;
case 3:
cout << " ------------------查找身份证信息---------------------" << endl;
cout << "请输入要查找的身份证号码:";
cin >> data.id;
search = searchBST(data, node);
if (data.id.length() != 18) {
cout << "格式错误\n";
isTrue(1);
break;
}
if (search == nullptr) {
cout << "身份证号码不存在\n";
isTrue(1);
break;
}
else {
cout << "身份证号码存在\n";
cout << "身份证:" << search->data.id << endl;
cout << "姓名:" << search->data.name << endl;
cout << "年龄:" << search->data.age << endl;
cout << "签发日期:" << search->data.time << endl;
cout << endl;
isTrue(1);
break;
}
if (data.id.length() != 18) {
cout << "格式错误\n";
isTrue(1);
break;
}
case 4:
cout << " ------------------修改身份证信息---------------------" << endl;
cout << "请输入要修改的身份证号码:";
cin >> data.id;
modifyBST(data, node);
isTrue(1);
break;
case 5:
cout << " ------------------展示身份证信息---------------------" << endl;
cout << "身份证 姓名 年龄 签发日期:" << endl;
inorderTraversal(node);
cout << endl;
isTrue(1);
break;
case 6:
{ cout << " ------------------导出身份证信息---------------------" << endl;
cout << " 正在导出中,请等待.........." << endl;
ofstream outfile("person_out.txt");
if (!outfile.is_open()) {
cout << "身份证信息导出失败." << endl;
return 1;
}
// 中序遍历二叉树,并将结果保存到文件中
outfiletxt(node, outfile);
// 关闭输出文件
outfile.close();
cout << "身份证信息导出成功!\n";
isTrue(1);
break; }
case 7: {
// 从文件导入数据
cout << " ------------------导入身份证信息---------------------" << endl;
string filename;
cout << "请输入要导入的文件名:";
cin >> filename;
cout << " 正在导入身份证信息,请等待........" << endl;
infiletxt(node, filename);
isTrue(1);
break;
}
case 8:
exit(0);
default:
cout << "无效的选择,请重新输入!" << endl;
break;
}
}
return 0;
}
}
}
使用时要注意几个点:
1.要注意导入文档,要提前在根目录下创建相应名字的txt文档
2.txt文档中要注意删除多余的空格,并按照正确的格式输入各种信息
比如
446666666666666666,王大力,19,2003.08
3.创建过的账号密码会保存在account文档中,下一次也可以使用,不需要重新创建