BAMS Version 1.0


前言

BAMS Version 1.0系列更新日志
【2020.8.6】 BAMS V1.0/1.1
【2020.8.11】 BAMS V1.2
【2020.8.13】 BAMS V1.3
【2020.9.10】 BAMS V1.4


在编写这个系统的过程中,参考了以下资料和站上一些大佬的文档:
CSDN-C语言实现数据库操作
CSDN-C语言文件操作
CSDN-从B树、B+树、B*树谈到R 树
CSDN-C语言使用文件系统模拟数据库

一、BAMS基本功能实现介绍

BAMS是基于txt文件建构的模拟数据库,其中实现了基本的数据库/数据表创建、删除、查找功能,详细可见如下:

  1. 系统登录
  2. 数据库创建
  3. 数据库删除
  4. 数据库查找
  5. 代码数据文件创建
  6. 代码数据文件删除
  7. 代码数据文件查找/复制

BAMS这个是最开始的设计图,其实也是期末作业当时做的design。BAMS系统其实就是在这个基础上改进而来:

  1. 删除了代码文件修改功能 这里包括了原先的文件内容单独修改、增加、删除。因为在初始修改的时候,考虑到用户(一般是我自己)使用的时候一般已经有IDE处理好代码,这个系统无需再去提供编辑功能,而且这些功能容易对代码的缩进等重要信息产生错误编辑,故不考虑在系统中对文件进行编辑。
  2. 修改了代码数据读取/输出的方式 在原先的系统中,数据是按照表-行的方式进行输入和读取的。但当其应用到代码读取时,用户不可能一行一行地将代码重新输入系统界面。因此,BAMS系统新增了剪贴板的读取功能,该功能能够读取用户复制后存储在剪贴板的代码块,并一次性将其输入文件。该方法还应用于代码从文件的读取和显示。
  3. 优化了交互界面 虽然还是采用了命令行界面作为系统的交互界面,但是在信息提示区、显示区和输入区之间做了分界,优化了用户的体验。

这些功能的实现其实依赖于非常基本的C语言函数。详细使用方法已经写在了代码之中,在此不再过多赘述

在数据库-数据文件查询中,嵌入了B树的构建与查找功能,以希望提高文件的查找效率,包括了B树的构建,删除;结点的创建,插入,删除;结点递归查找等功能。

考虑到用户可能需要调用已经存在的代码数据,自V1.3开始,增加了一键复制代码数据到剪贴板的功能。

在BAMS V1.0完成时,这个系统还是存在着很多的问题,包括但不限于:

  1. B树算法依然存在一些问题
  2. 对内存的占用可能在未知的情况下突然增大
  3. 用户交互界面8太行
  4. 需要小心保存所在文件夹文件,防止被修改
  5. 使用了自动读取剪贴板,可能造成读取错误
  6. 部分代码比较冗杂,需要考虑复用
  7. 操作比较复杂(只适合我自己的习惯)

经过了两个月大约300余次的测试,解决了B树算法上存在的问题,对用户界面也做了一些完善。对于文件保存的问题,可以独立设置文件夹的权限进行保护;操作复杂性其实也是可以接受的。但是剪贴板读取复制出现了一点问题,但是还没有找到原因。在之后的测试中希望能找出来。

特别需要注意的是在使用BAMS系统时,尽量按照程序提示进行操作,以防出现编码错误。不要私自篡改注册表文件和库文件,以防程序读取出现问题。 ·代码文件与程序界面显示的数据可能存在出入,代码数据文件信息以程序界面的为准。


二、BAMS具体技术细节

1.主模块 MAIN.c

代码如下:

#include "Database.h"
#include "Btree.h"

int main()  //----主函数----//
{
	int temp = 0;
	while (temp < MAXTIME)  //无论账号还是密码,顶多只有3次机会,超出则程序自动关闭
	{
		if (DT_Login())
		{
			while (DT_Database_Func());
			break;
		}
		temp++;
	}
	if (temp == MAXTIME)
	{
		system("cls");
		printf("-----------------------------------\n");
		printf("已达到输入次数上限,程序即将自动关闭\n");
		printf("-----------------------------------\n");
		Sleep(2000);
		return 0;
	}
	system("cls");
	printf("-----------------------------------\n");
	printf("感谢您的使用\n");
	printf("-----------------------------------\n");
	return 0;
}

2.模拟数据库模块 DATABASE.h

代码如下:

2.0 前置声明

/**
 * Code by C
 * @version 1.40 2020-09-10
 * @author MPX
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<windows.h>
#include<conio.h>
#include<ctype.h>
#include<errno.h>

#include "Btree.h"
#include "GetPlate.h"

#define MAXLEN 10	//	最大输入长度
#define MAXTIME 3	//	最大尝试次数

FILE* fp;
FILE* fp2;
FILE* fp3;
FILE* fp4;
FILE* fp5; //注册表文件
FILE* fp6; //注册表修改临时文件

//登录
int DT_Login();
//代码数据库界面
int DT_Database_Func();
//代码数据表界面
int DT_Table_Func(char* db_name);
//创建数据库
int DT_Database_Create();
//删除数据库
int DT_Database_Delete();
//调用数据库
int DT_Database_Open();
//创建代码数据文件
int DT_Table_Create(char* db_url, BTree* T, BTree_node* node, char* setting);
//删除代码数据文件
int DT_Table_Delete(char* db_url, BTree* T, BTree_node* node, char* seeting);
//查询代码数据文件
int DT_Data_Select(char* db_url, BTree* T, BTree_node* node); 
//编码转换
int DT_AutoCode(char RID[]); 

2.1 系统登录 Login

用户输入相应的用户名和密码,如果成功匹配则进入数据库界面;否则提示用户出错并提供重试机会(最大重试机会为3次)。

int DT_Login()
{
	system("cls");
	int i = 0, flag = 0;
	char ch = 0;
	char password[MAXLEN] = { 0 }, username[MAXLEN] = { 0 };
	char true_password[] = "111", true_username[] = "111";

	printf("-----------------------------------\n");
	printf("< 代码数据管理系统  Version 1.40  >\n");
	printf("<     最后更新日期: 2020.9.10    >\n");
	printf("-----------------------------------\n");
	printf("请您先登录\n");
	printf("-----------------------------------\n");
	printf("请输入账号:");
	scanf_s("%s", username, MAXLEN);  //scanf_s必须严格限定读入字符大小,否则就会报错

	if (!strcmp(username, true_username))  //CASE_1:用户名输入正确
	{
		printf("请输入密码:");
		scanf_s("%s", password, MAXLEN); 
		if (!strcmp(password, true_password))  //CASE_1_1:密码输入正确
		{
			return 1;
		}
		else  //CASE_1_2:密码输入错误
		{
			printf("-----------------------------------\n");
			printf("密码输入错误!\n");
			printf("-----------------------------------\n");
			printf("您有以下两个选择:\n");
			printf("1.再次尝试\n2.退出程序\n");
			printf("-----------------------------------\n");//35标准格式
			printf("请输入您的选择");
			scanf_s("%d", &flag);
			if (flag == 1) return 0;
			else exit(0);
		}
	}
	else  //CASE_2:用户名输入错误
	{
		printf("-----------------------------------\n");
		printf("用户不存在!\n");
		printf("-----------------------------------\n");
		printf("您有以下两个选择:\n");
		printf("1.再次输入账号\n2.退出程序\n");
		printf("-----------------------------------\n");
		printf("请输入您的选择:");
		scanf_s("%d", &flag);
		if (flag == 1) return 0;
		else exit(0);
	}
}

2.2 数据库菜单 Database_Menu

int DT_Database_Func()
{
	system("cls");
	int flag = 0, flag_1 = 0, func = 0;

	printf("-----------------------------------\n");
	printf("功能界面\n");
	printf("-----------------------------------\n");
	printf("1.创建代码数据库\n2.删除代码数据库\n3.调用代码数据库\n4.退出程序\n");
	printf("-----------------------------------\n");
	while (flag_1 == 0)
	{
		printf("请输入需要使用的功能序号:");
		scanf_s("%d", &func);
		if (func <= 4 && func >= 1)
		{
			flag_1 = 1;
			break;
		}
		else
		{
			printf("-----------------------------------\n");
			printf("无效输入!\n");
		}
	}
	

	switch (func)
	{
	case 1: while (DT_Database_Create()); break;  //CASE_1:创建
	case 2: while (DT_Database_Delete()); break;  //CASE_2:删除
	case 3: while (DT_Database_Open()); break;  //CASE_3:调用
	case 4: exit(0); break;  //CASE_4:退出程序
	}

	/*
	system("cls");  //在使用完上述功能后,判断用户是否还要继续操作
	printf("-----------------------------------\n");
	printf("您有以下选择:\n");
	printf("1.继续使用\n2.退出程序\n");
	printf("-----------------------------------\n");
	printf("请输入您的选择:");
	scanf_s("%d", &flag);
	if (flag == 1) return 1;  //CASE_1:继续使用
	if (flag == 2) return 0;  //CASE_2:退出程序
	*/
	return 1;
}

2.3 数据表菜单 Table_Menu

int DT_Table_Func(char* db_url)
{
	system("cls");
	int flag = 0, func = 0, count = 0, flag_1 = 0;
	char db_name[13] = { 0 }, SID[10], haven[16], file_haven[45]; char* p;
	char setting[45];
	errno_t err, err1;
	BTree T;

	BTree_CreateTree(&T, 3);
	err = fopen_s(&fp, db_url, "r");  //不需要重复写这句话,因为err在条件时已经被赋值,如果写下这句话,会显示文件被占用从而使err的值发生改变【已修复】
	fseek(fp, 0L, SEEK_SET);

	while (fgets(haven, 16, fp) != NULL)  //读取数据库文件中的文件名,在内存中生成B树
	{
		strcpy_s(SID, strlen(haven) + 1, haven);  //分离尾缀
		int key = atoi(SID);
		BTree_Insert(&T, key);
	}
	fclose(fp);

	printf("-----------------------------------\n");
	strcpy_s(db_name, strlen(strtok_s(db_url, ".txt", &p)) + 1, strtok_s(db_url, ".txt", &p));
	printf("%s\n", db_name);
	
	//独立注册表设置
	strcpy_s(setting, strlen(db_name) + 1, db_name);
	strcat_s(setting, strlen(setting) + strlen("程序注册表.txt") + 1, "程序注册表.txt");

	if ((err = fopen_s(&fp5, setting, "r")) == 0) {
		printf("-----------------------------------\n");
		printf("----< 代码数据文件注册表 >----\n");
		printf("-----------------------------------\n");
		while (fgets(file_haven, 45, fp5) != NULL) {
			printf("%s", file_haven);
			count++;
		}
		fclose(fp5);
	}
	printf("-----------------------------------\n");
	printf("该库内总代码数据文件数:%d\n", count);

	printf("-----------------------------------\n");
	printf("1.创建代码数据文件\n2.删除代码数据文件\n3.查找代码数据文件\n4.添加代码【暂不支持】\n5.删除代码【暂不支持】\n6.修改代码【暂不支持】\n7.退出程序\n");
	while (flag_1 == 0)
	{
		printf("请输入需要使用的功能序号:");
		scanf_s("%d", &func);
		if (func <= 3 && func >= 1)
		{
			flag_1 = 1;
			break;
		}
		else
		{
			printf("-----------------------------------\n");
			printf("无效输入!\n");
		}
	}

	switch (func)
	{
	case 1: while (DT_Table_Create(db_url, &T, T.root, setting)); break;  //CASE_1:创建代码数据文件
	case 2: while (DT_Table_Delete(db_url, &T, T.root, setting)); break;  //CASE_2:删除代码数据文件
	case 3: while (DT_Data_Select(db_url, &T, T.root)); break;  //CASE_3:查询代码数据文件
	case 4: break;  //CASE_4:添加数据【暂不支持】
	case 5: break;  //CASE_5:删除数据【暂不支持】
	case 6: //CASE_6:修改数据【暂不支持】
	case 7: exit(0); break;  //CASE_7:退出程序
	}

	system("cls");
	strcat_s(db_name, strlen(db_name) + strlen(".txt") + 1, ".txt");
	err1 = fopen_s(&fp4, "db_temp.txt", "w");  //更新数据库文件内容
	BTree_Traverse(&T, T.root, 0);
	remove(db_name);
	fclose(fp4);
	rename("db_temp.txt", db_name);

	printf("-----------------------------------\n");
	printf("1.继续使用\n2.退出程序\n");
	printf("-----------------------------------\n");
	printf("请输入您的选择:");
	scanf_s("%d", &flag);
	if (flag == 1) return 0;  //CASE_1:继续使用
	if (flag == 2) exit(0);  //CASE_3:退出程序
	return 0;
}

2.4 数据库创建 Database_Create

int DT_Database_Create()    //----数据库创建----//  用户根据提示进行数据库创建操作
{
	system("cls");
	int flag = 0;
	char db_name[10] = { 0 }, db_url[18], setting[30];
	errno_t err, err1;

	printf("-----------------------------------\n");
	printf("新建代码库\n");
	printf("-----------------------------------\n");
	printf("请输入代码库的名字(10个字以内):");
	scanf_s("%s", db_name, 10);
	strcpy_s(db_url, strlen(db_name) + 1, db_name);
	strcat_s(db_url, strlen(db_url) + strlen("代码库") + 1, "代码库");

	strcpy_s(setting, strlen(db_url) + 1, db_url);
	strcat_s(setting, strlen(setting) + strlen("程序注册表.txt") + 1, "程序注册表.txt");
	err1 = fopen_s(&fp5, setting, "w");
	fclose(fp5);

	strcat_s(db_url, strlen(db_url) + strlen(".txt") + 1, ".txt");

	if ((err = fopen_s(&fp, db_url, "r")) != 0)
	{
		err = fopen_s(&fp, db_url, "w");
		fclose(fp);
		printf("-----------------------------------\n");
		printf("创建成功!\n");
		Sleep(1000);
		return 0;
	}
	else
	{
		system("cls");
		printf("-----------------------------------\n");
		printf("Error:该代码库已经被创建,或存在命名错误\n");
		printf("-----------------------------------\n");
		printf("您有以下选择:\n");
		printf("1.重新创建\n2.取消创建\n3.退出程序\n");
		printf("-----------------------------------\n");
		printf("请输入你的选择:");
		scanf_s("%d", &flag);
		if (flag == 1) return 1;
		if (flag == 2) return 0;
		if (flag == 3) exit(0);
		else
		{
			printf("-----------------------------------\n");
			printf("没有该选项\n结束创建");
			Sleep(1000);
			return 0;
		}
	}
}

2.5 数据库删除 Database_Delete

int DT_Database_Delete()  //----数据库删除(二级界面)----//  用户根据提示进行数据库删除操作
{
	system("cls");
	int flag = 0;
	char db_name[10] = { 0 }, db_url[18], haven[18], setting[30];
	//size_t errmsglen = 100;//strerrorlen_s(errno) + 1;
	char errmsg[100];
	errno_t err;

	printf("-----------------------------------\n");
	printf("删除代码库\n");
	printf("-----------------------------------\n");
	printf("请输入需要删除的代码库名:");
	scanf_s("%s", db_name, 10);
	strcpy_s(db_url, strlen(db_name) + 1, db_name);
	strcat_s(db_url, strlen(db_url) + strlen("代码库") + 1, "代码库");

	strcpy_s(setting, strlen(db_url) + 1, db_url);
	strcat_s(setting, strlen(setting) + strlen("程序注册表.txt") + 1, "程序注册表.txt");
	remove(setting);

	strcat_s(db_url, strlen(db_url) + strlen(".txt") + 1, ".txt");

	err = fopen_s(&fp, db_url, "r");  //对相关的库进行遍历删除
	while (fscanf_s(fp, "%s", haven, 18) != EOF) remove(haven);  //使用了fscanf函数用于防止程序读入\n造成错误
	fclose(fp);

	if (!(remove(db_url)))
	{
		printf("-----------------------------------\n");
		printf("删除成功\n");
		printf("-----------------------------------\n");
		Sleep(1000);
		return 0;
	}
	else
	{
		system("cls");
		printf("-----------------------------------\n");
		printf("在本次运行打开过的库删除会失败,建议退出程序再运行删除。\n");  //程序漏洞提示
		printf("-----------------------------------\n");
		printf("您有以下选择:\n");
		printf("1.再次删除\n2.取消删除\n3.退出程序\n");
		printf("-----------------------------------\n");
		printf("请输入你的选择:\n");
		scanf_s("%d", &flag);
		if (flag == 1) return 1;
		if (flag == 2) return 0;
		if (flag == 3) exit(0);
		else
		{
			printf("-----------------------------------\n");
			printf("没有该选项\n结束删除");
			Sleep(1000);
			return 0;
		}
	}
}

2.6 数据库调用 Database_Open

int DT_Database_Open()  //----数据库打开----//  数据库三级界面入口
{
	system("cls");
	int flag = 0;
	char db_name[10] = { 0 }, db_url[18];
	errno_t err;

	printf("-----------------------------------\n");
	printf("打开代码库\n");
	printf("-----------------------------------\n");
	printf("请输入需要打开的代码库名:");
	scanf_s("%s", db_name, 10);
	strcpy_s(db_url, strlen(db_name) + 1, db_name);
	strcat_s(db_url, strlen(db_url) + strlen("代码库") + 1, "代码库");
	strcat_s(db_url, strlen(db_url) + strlen(".txt") + 1, ".txt");

	if ((err = fopen_s(&fp, db_url, "r")) == 0)  //判断数据库是否存在,若存在则进入数据库操作三级界面,否则返回不存在提示并令用户选择下一步操作
	{
		fclose(fp);
		printf("-----------------------------------\n");
		printf("成功打开代码库\n");
		Sleep(1000);
		while (DT_Table_Func(db_url));//?
		return 0;
	}
	else
	{
		system("cls");
		printf("-----------------------------------\n");
		printf("该库不存在\n");
		printf("-----------------------------------\n");
		printf("您有以下选择:\n");
		printf("1.打开另一个代码库\n2.取消打开\n3.退出程序\n");
		printf("-----------------------------------\n");
		printf("请输入您的选择:");
		scanf_s("%d", &flag);
		if (flag == 1) return 1;  //继续使用
		if (flag == 2) return 0;  //取消打开,跳出循环
		if (flag == 3) exit(0);  //退出程序
		else
		{
			printf("-----------------------------------\n");
			printf("没有该选项\n结束打开");
			Sleep(1000);
			return 0;
		}
	}
}

2.7 代码数据文件创建 Table_Create

int DT_Table_Create(char* db_url, BTree* T, BTree_node* node, char* setting)  //----数据表创建模块(四级界面)----//  用户根据提示创建数据表
{
	system("cls");
	int flag = 0, n = 0, i = 0, line;
	char research_id[10] = { 0 };
	char Rid[14], file_name[30], file_url[45], code[200];
	char* p;
	errno_t err;

	
	printf("-----------------------------------\n");
	printf("新建代码数据文件\n");
	printf("-----------------------------------\n");
	printf("标准格式:202001-01\n");
	printf("请按照格式输入代码数据文件的编号:");
	rewind(stdin);
	scanf_s("%s", research_id, 10);
	strcpy_s(Rid, strlen("TEST") + 1, "TEST");
	strcat_s(Rid, strlen(Rid) + strlen(research_id) + 1, research_id);
	int key = DT_AutoCode(research_id);

	printf("请输入代码数据文件的标题(10字以内):");
	scanf_s("%s", file_name, 30);
	strtok_s(db_url, "代码库", &p);
	strcpy_s(file_url, strlen(db_url) + 1, db_url);
	strcat_s(file_url, strlen(file_url) + strlen("-") + 1, "-");
	strcat_s(file_url, strlen(file_url) + strlen(Rid) + 1, Rid);
	strcat_s(file_url, strlen(file_url) + strlen(file_name) + 1, file_name);
	strcat_s(file_url, strlen(file_url) + strlen(".txt") + 1, ".txt");

	err = fopen_s(&fp5, setting, "a+");
	fprintf(fp5, "%s\n", file_url);
	fclose(fp5);

	if (BTree_Search(&T, node, key, 0) == -1)
	{
		BTree_Insert(T, key);
		err = fopen_s(&fp2, file_url, "w");  //以只写方式打开,若文件不存在则建立文件
		printf("-----------------------------------\n");
		printf("请输入数据(可复制):\n");
		printf("----------------------------------\n");
		GetPlateString(fp2);
		fclose(fp2);
		fflush(stdin);
		printf("创建成功\n");
		printf("-----------------------------------\n");
		printf("按下任意键继续......");
		_getch();
		return 0;
	}
	else
	{
		system("cls");
		printf("-----------------------------------\n");
		printf("该数据表名称已经存在,或命名错误\n");
		printf("-----------------------------------\n");
		printf("您有以下选择:\n");
		printf("1.重新创建数据表\n2.取消创建数据表\n3.退出程序\n");
		printf("-----------------------------------\n");
		printf("请输入您的选择:");
		scanf_s("%d", &flag);
		if (flag == 1) return 1;  //CASE_1:重新创建
		if (flag == 2) return 0;  //CASE_2:取消创建
		if (flag == 3) exit(0);  //CASE_3:退出程序
		else
		{
			printf("-----------------------------------\n");
			printf("选项不存在!\n取消创建数据表");
			Sleep(2000);
			return 0;
		}
	}
}

2.8 代码数据文件删除 Table_Delete

int DT_Table_Delete(char* db_url, BTree* T, BTree_node* node, char* setting)  //----数据表删除模块----//
{
	system("cls");
	int flag;
	errno_t err, err1;
	char research_id[10] = { 0 };
	char Rid[14], file_name[30], file_url[45], haven[45], code[200];
	char* p;

	printf("-----------------------------------\n");
	printf("删除代码数据文件\n");
	printf("-----------------------------------\n");
	printf("标准格式:202001-01\n");
	printf("请按照格式输入代码数据文件的编号:");
	rewind(stdin);
	scanf_s("%s", research_id, 10);
	strcpy_s(Rid, strlen("TEST") + 1, "TEST");
	strcat_s(Rid, strlen(Rid) + strlen(research_id) + 1, research_id);
	int key = DT_AutoCode(research_id);

	printf("请输入代码数据文件的标题(10字以内):");
	scanf_s("%s", file_name, 30);
	strtok_s(db_url, "代码库", &p);
	strcpy_s(file_url, strlen(db_url) + 1, db_url);
	strcat_s(file_url, strlen(file_url) + strlen("-") + 1, "-");
	strcat_s(file_url, strlen(file_url) + strlen(Rid) + 1, Rid);
	strcat_s(file_url, strlen(file_url) + strlen(file_name) + 1, file_name);
	strcat_s(file_url, strlen(file_url) + strlen(".txt") + 1, ".txt");

	BTree_Print(&T, T->root, 0);
	if (BTree_Search(&T, node, key, 0) != -1)
	{
		printf("-----------------------------------\n");
		printf("该代码数据文件存在!\n");
		printf("B树查询编号 SID :%d\n", key);
		printf("代码数据文件名 RID :%s\n", file_url);
		printf("-----------------------------------\n");

		err = fopen_s(&fp5, setting, "r");
		err1 = fopen_s(&fp6, "_temp.txt", "w");
		while (fscanf_s(fp5, "%s\n", haven, 45) != EOF)
		{
			if (strcmp(haven, file_url) == 0)
			{
				continue;
			}
			else fprintf(fp6, "%s\n", haven);
		}
		fclose(fp5);
		remove(setting);
		fclose(fp6);  //先关闭文件,后修改名字
		rename("_temp.txt", setting);

		if (!(remove(file_url)))  //若删除成功则返回0,此处还没有进行测试
		{
			BTree_DeleteTree(T, key);
			printf("删除成功!\n");
			printf("-----------------------------------\n");
			printf("按下任意键继续......");
			_getch();
			return 0;
		}
		else  //删除失败
		{
			system("cls");
			printf("-----------------------------------\n");
			printf("删除代码数据文件出错!\n");
			printf("-----------------------------------\n");
			printf("您有以下选择:\n");
			printf("1.重新删除\n2.结束删除\n3.退出程序\n");
			printf("-----------------------------------\n");
			printf("请输入您的选择:");
			scanf_s("%d", &flag);
			if (flag == 1) return 1;
			if (flag == 2) return 0;
			if (flag == 3) exit(0);
			else
			{
				printf("-----------------------------------\n");
				printf("没有该选项\n结束删除");
				//Sleep(1000);
				return 0;
			}
		}
	}
	  //CASE_2:该数据表不在数据库内
	system("cls");
	printf("-----------------------------------\n");
	printf("该代码数据文件不存在于库!\n");
	printf("-----------------------------------\n");
	printf("您有以下选择:\n");
	printf("1.删除其他\n2.取消删除\n3.退出程序\n");
	printf("-----------------------------------\n");
	printf("请输入您的选择:");
	scanf_s("%d", &flag);
	if (flag == 1) return 1;
	if (flag == 2) return 0;
	if (flag == 3) exit(0);
	else
	{
		printf("-----------------------------------\n");
		printf("没有该选项\n取消删除");
		Sleep(2000);
		return 0;
	}
}

2.9 代码数据文件调用 Table_Select

int DT_Data_Select(char* db_url, BTree* T, BTree_node* node)
{
	system("cls");
	int flag = 0, flag1;
	int a;
	char* p;
	char research_id[10] = { 0 };
	char Rid[14], file_name[30], file_url[45], code[1000];
	errno_t err;

	printf("-----------------------------------\n");
	printf("查找代码数据\n");
	printf("-----------------------------------\n");
	printf("标准格式:202001-01\n");
	printf("请按照格式输入代码数据文件的编号:");

	scanf_s("%s", research_id, 10);
	strcpy_s(Rid, strlen("TEST") + 1, "TEST");
	strcat_s(Rid, strlen(Rid) + strlen(research_id) + 1, research_id);
	int key = DT_AutoCode(research_id);

	printf("请输入代码数据文件的标题(10字以内):");
	scanf_s("%s", file_name, 30);
	strtok_s(db_url, "代码库", &p);
	strcpy_s(file_url, strlen(db_url) + 1, db_url);
	strcat_s(file_url, strlen(file_url) + strlen("-") + 1, "-");
	strcat_s(file_url, strlen(file_url) + strlen(Rid) + 1, Rid);
	strcat_s(file_url, strlen(file_url) + strlen(file_name) + 1, file_name);
	strcat_s(file_url, strlen(file_url) + strlen(".txt") + 1, ".txt");

	if (BTree_Search(&T, node, key, 0) != -1)
	{
		printf("-----------------------------------\n");
		printf("代码数据文件存在!\n");
		printf("B树查询编号 SID :%d\n", key);
		printf("代码数据文件名 RID :%s\n", file_url);
		printf("-----------------------------------\n");
		if ((err = fopen_s(&fp2, file_url, "r") == 0))
		{
			while (fgets(code, 1000, fp2) != NULL)
			{
				printf("%s", code);
			}
			printf("查询代码成功!\n");
			//fclose(fp2);
			printf("-----------------------------------\n");

			printf("是否需要复制代码数据?\n1.需要复制\n2.不需要\n");
			printf("-----------------------------------\n");
			printf("请输入您的选择:");
			scanf_s("%d", &flag1);
			if (flag1 == 1) StringAutoCopy(fp2);

			fclose(fp2);
			printf("按下任意键继续......");
			_getch();
			return 0;
		}
		else
		{
			system("cls");
			printf("-----------------------------------\n");
			printf("查询代码数据文件出错!\n");
			printf("-----------------------------------\n");
			printf("您有以下选择:\n");
			printf("1.重新查询\n2.结束查询\n3.退出程序\n");
			printf("-----------------------------------\n");
			printf("请输入您的选择:");
			scanf_s("%d", &flag);
			if (flag == 1) return 1;
			if (flag == 2) return 0;
			if (flag == 3) exit(0);
			else
			{
				printf("没有该选项\n结束查询");
				return 0;
			}
		}
	}
	system("cls");
	printf("-----------------------------------\n");
	printf("该代码数据文件不存在于该数据库!\n");
	printf("-----------------------------------\n");
	printf("您有以下选择:\n");
	printf("1.查询其他\n2.取消查询\n3.退出程序\n");
	printf("-----------------------------------\n");
	printf("请输入您的选择:");
	scanf_s("%d", &flag);
	if (flag == 1) return 1;
	if (flag == 2) return 0;
	if (flag == 3) exit(0);
	else
	{
		printf("-----------------------------------\n");
		printf("没有该选项\n结束查询");
		Sleep(1000);
		return 0;
	}
}

2.10 自动转码 AutoCode

int DT_AutoCode(char RID[])  //----自动编码----//  用户输入查询编号,自动将查询编号 RID 转换为搜索编号 SID
{
	int i,j;
    for(i = 0, j = 0; *(RID + i) != '\0'; i++)
    {
		if (*(RID + i) == '-')
			continue;
        else
        {
			*(RID + j) = *(RID + i);
            j++;
        }
    }
	*(RID + j) = '\0';

	int SID = atoi(RID);
	return SID;
}

3.B树查找模块

3.0 前置声明

/**
 * Code by C
 * @version 1.40 2020-09-10
 * @author MPX
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

#define MaxM 10   //B树最大阶数


typedef struct BTree_node {  //B树和B树结点类型
    int* keys;              //结点关键字指针
    struct BTree_node** childrens;  //孩子指针
    int num;                //关键字个数
    int leaf;               //叶结点
}BTree_node;

typedef struct BTree {
    BTree_node* root;
    int t;
}BTree;

//查找(k为要查找的内容)
int BTree_Search(BTree* T, BTree_node* node, int k, int layer); 
//创建B树结点
BTree_node* BTree_CreateNode(int t, int leaf);  
//创建B树
void BTree_CreateTree(BTree* T, int t);  
//摧毁B树结点
void BTree_Destory(BTree_node* node); 
//分裂结点x的第i个孩子
void BTree_SplitChild(BTree* T, BTree_node* x, int i);  
//插入前对关键字的排序
void BTree_Insert_Nonfull(BTree* T, BTree_node* x, int k);  
//在B树中插入关键字
void BTree_Insert(BTree* T, int key);  
//遍历B树并将其结点输入文件
void BTree_Traverse(BTree* T, BTree_node* node, int layer);  
//打印显示整棵树
void BTree_Print(BTree* T, BTree_node* node, int layer);  
//B树删除关键字
void BTree_DeleteKey(BTree* T, BTree_node* node, int key);     
//删除B树
int BTree_DeleteTree(BTree* T, int key);   

3.1 递归查找 Node_Search

int BTree_Search(BTree* T, BTree_node* node, int k, int layer) // k为要查找的内容,layer默认为0,查找成功返回关键字结点(int),失败返回-1
{
    BTree_node* p = node;
    int  i;
    if (p)
    {
        for (i = 0;i < node->num;i++)
        {
            if (p->keys[i] == k)  
                return p->keys[i];
        }
        layer++;
        for (i = 0;i <= p->num;i++)
        {
            if (p->childrens[i])
            {
                if ((BTree_Search(T, p->childrens[i], k, layer)) != -1)
                {
                    return BTree_Search(T, p->childrens[i], k, layer);
                    break;
                }
                else
                    continue;
            }
        }
    }
    return -1;
}

3.2 创建B树结点 Node_Create

BTree_node* BTree_CreateNode(int t, int leaf)  //创建B树结点
{
    BTree_node* node = (BTree_node*)calloc(1, sizeof(BTree_node));  //分配内存空间,返回指向指针,并设置分配的内存为0
    if (node == NULL) assert(0);  //不存在结点,程序停止运行

    node->leaf = leaf;
    node->keys = (int*)calloc(1, (2 * t - 1) * sizeof(int));  //给键结点分配内存用尽
    node->childrens = (BTree_node**)calloc(1, (2 * t) * sizeof(BTree_node));  //给孩子分配内存空间
    node->num = 0;

    return node;
}

3.3 创建B树 Tree_Create

void BTree_CreateTree(BTree* T, int t)   //创建B树
{
    T->t = t;
    BTree_node* x = BTree_CreateNode(t, 1);
    T->root = x;
}

3.4 删除B树结点 Node_Delete

void BTree_Destory(BTree_node* node) //摧毁B树
{
    assert(node);  //若存在node,则向下执行;若不存在,终止程序

    free(node->childrens);
    free(node->keys);
    free(node);
}

3.5 分裂结点的孩子 Node_SpiltChild

void BTree_SplitChild(BTree* T, BTree_node* x, int i)  //分裂结点x的第i个孩子
{
    int t = T->t;
    BTree_node* y = x->childrens[i];
    BTree_node* z = BTree_CreateNode(t, y->leaf);  //创建一个B树结点

    z->num = t - 1;
    int j = 0;
    for (j = 0;j < t - 1;j++)
    {
        z->keys[j] = y->keys[j + t];
    }
    if (y->leaf == 0)  //如果叶结点为0,将孩子结点下移一层
    {
        for (j = 0;j < t;j++)
        {
            z->childrens[j] = y->childrens[j + t];
        }
    }
    y->num = t - 1;
    for (j = x->num;j >= i + 1;j--)
    {
        x->childrens[j + 1] = x->childrens[j];
    }
    x->childrens[i + 1] = z;
    for (j = x->num - 1;j >= i;j--)
    {
        x->keys[j + 1] = x->keys[j];
    }
    x->keys[i] = y->keys[t - 1];
    x->num += 1;
}

3.6 关键字排序 Key_Sort

void BTree_Insert_Nonfull(BTree* T, BTree_node* x, int k) //插入前对关键字的排序
{
    int i = x->num - 1;    //i为关键字个数-1,数组中即为关键字对应下标
    if (x->leaf == 1)
    {
        while (i >= 0 && x->keys[i] > k)  //比较前后关键字大小,排序
        {
            x->keys[i + 1] = x->keys[i];
            i--;
        }
        x->keys[i + 1] = k;
        x->num += 1;
    }
    else
    {
        while (i >= 0 && x->keys[i] > k)
            i--;
        if (x->childrens[i + 1]->num == (2 * (T->t)) - 1)
        {
            BTree_SplitChild(T, x, i + 1);     //分裂结点
            if (k > x->keys[i + 1])
                i++;
        }
        BTree_Insert_Nonfull(T, x->childrens[i + 1], k);  //递归,分裂后重新排序关键字
    }
}

3.7 关键字插入 Key_Insert

void BTree_Insert(BTree* T, int key)  //在B树中插入关键字
{
    BTree_node* r = T->root;
    if (r->num == 2 * T->t - 1)   // 若该结点中关键码个数等于阶数-1,则将引起结点的分裂。以中间关键码为界将结点一分为二,产生一个新结点,并把中间关键码插入到父结点(k-1层)中
    {
        BTree_node* node = BTree_CreateNode(T->t, 0);
        T->root = node;

        node->childrens[0] = r;
        BTree_SplitChild(T, node, 0);     //插入叶子结点后不满足B树性质,需要对叶子结点进行分裂
        int i = 0;
        if (node->keys[0] < key)
            i++;
        BTree_Insert_Nonfull(T, node->childrens[i], key);
    }
    else
    {
        BTree_Insert_Nonfull(T, r, key);   //关键码个数小于阶数-1,直接插入
    }
}

3.8 遍历B树输出 Tree_Traverse

FILE* fp;

void BTree_Traverse(BTree* T, BTree_node* node, int layer)  //遍历B树写入库文件
{
    BTree_node* p = node;
    int  i;
    if (p) {
        for (i = 0;i < node->num;i++)
            fprintf(fp, "%d\n", node->keys[i]);  //写入文件
        layer++;
        for (i = 0;i <= p->num;i++)
            if (p->childrens[i])
                BTree_Traverse(T, p->childrens[i], layer);
    }
    else printf("空\n");
}

3.9 打印B树(测试用)Tree_Print

void BTree_Print(BTree* T, BTree_node* node, int layer)
{
    BTree_node* p = node;
    int  i;
    if (p) {
        printf("\n层数 = %d 关键字个数 = %d 是否叶结点 = %d\n", layer, p->num, p->leaf);
        for (i = 0;i < node->num;i++)
            printf("%d ", p->keys[i]);
        printf("\n");
        layer++;
        for (i = 0;i <= p->num;i++)
            if (p->childrens[i])
                BTree_Print(T, p->childrens[i], layer);
    }
    else printf("该树为空树\n");
}

3.10 关键字合并 Key_Merge

void BTree_Merge(BTree* T, BTree_node* node, int idx)  //把内结点node的idx个孩子和idx+1个孩子合并
{
    BTree_node* left = node->childrens[idx];  //第idx个孩子
    BTree_node* right = node->childrens[idx + 1];  //第idx+1个孩子

    int i = 0;
    left->keys[T->t - 1] = node->keys[idx];       //数据合并
    for (i = 0;i < T->t - 1;i++)
    {
        left->keys[T->t + i] = right->keys[i];
    }
    if (!left->leaf)
    {
        for (i = 0;i < T->t;i++) {
            left->childrens[T->t + i] = right->childrens[i];
        }
    }
    left->num += T->t;
    BTree_Destory(right);       //抹去右部分

    for (i = idx + 1;i < node->num;i++)
    {
        node->keys[i - 1] = node->keys[i];
        node->childrens[i] = node->childrens[i + 1];
    }
    node->childrens[i + 1] = NULL;
    node->num -= 1;

    if (node->num == 0)
    {
        T->root = left;
        BTree_Destory(node);
    }
}

3.12 关键字删除 Key_Delete

void BTree_DeleteKey(BTree* T, BTree_node* node, int key)     //B树删除关键字
{
    if (node == NULL) return;

    int idx = 0, i;
    while (idx<node->num && key>node->keys[idx])
    {
        idx++;
    }
    if (idx < node->num && key == node->keys[idx]) {
        if (node->leaf) {      //关键字 key 在结点 node 中,node是叶结点, 直接在 node 中删除 key
            for (i = idx;i < node->num - 1;i++)
            {
                node->keys[i] = node->keys[i + 1];
            }
            node->keys[node->num - 1] = 0;
            node->num--;

            if (node->num == 0)
            {
                free(node);
                T->root = NULL;
            }
            return;
        }
        else if (node->childrens[idx]->num >= T->t) 
        //结点node前于 key 的子结点 left 至少含有 t 个关键字, 找出 key 在以 left 为根的子树中的前驱 key-1, 递归地删除key-1, 
        //并在node中用key-1 代替key
        {
            BTree_node* left = node->childrens[idx];
            node->keys[idx] = left->keys[left->num - 1];
            BTree_DeleteKey(T, left, left->keys[left->num - 1]);
        }
        else if (node->childrens[idx + 1]->num >= T->t) 
        /*对称的情况, 如果left中少于 t-1 个结点, 检查结点 node 中后于 key 的子结点 right. 如果 right 有 t 个关键字, 
        则找出 key 在以 right 为根的子树中的后继 key+1. 递归地删除 key+1, 并在 node 中 用 key+1 代替 key*/
        {
            BTree_node* right = node->childrens[idx + 1];
            node->keys[idx] = right->keys[0];
            BTree_DeleteKey(T, right, right->keys[0]);
        }
        else 
        /*left和right都只是有t-1 个结点, 则把 key 和 right 合并进入结点 left, 此时的 node 失去了 key 和指向 right 的指针, 
        且 left 包含t-1个关键字. 释放right, 递归地从left中删除 key*/
        {
            BTree_Merge(T, node, idx);
            BTree_DeleteKey(T, node->childrens[idx], key);
        }
    }
    else 
    /*关键字 key 不在内部结点 node 中, 则必包含于node->childrens中. 如果 node->childrens 只有 t-1 个关键字, 
    那么执行这两步保证下降到一个至少包含 t 个关键字的结点*/
    {
        BTree_node* child = node->childrens[idx];
        if (child == NULL)
        {
            printf("无法删除键%d\n", key);
            return;
        }
        /*如果node->children只有 t-1 个关键字, 但它的兄弟至少有 t 个关键字, 则把node中的某个关
        键字下降到node->children中, 讲 node->children 的相邻左兄弟或右兄弟的一个关键字升至 node, 讲该兄弟
        中相应的孩子指针移到 node->children中, 使node->children增加一个关键字 */
        /*如果 node->children 以及 node->children 的所有兄弟结点都只有 t-1 个关键字, 则把 node->children 与一个兄弟合
        并, 即把 node 的一个关键字移到新合并的结点, 使之成为该结点的中间关键字*/
        if (child->num == T->t - 1)
        {
            BTree_node* left = NULL;
            BTree_node* right = NULL;
            if (idx - 1 >= 0)
                left = node->childrens[idx - 1];
            if (idx + 1 <= node->num)
                right = node->childrens[idx + 1];
            if ((left && left->num >= T->t) || (right && right->num >= T->t))
            {
                int richR = 0;
                if (right)
                    richR = 1;
                if (left && right) {
                    if (right->num > left->num)
                        richR = 1;
                    else richR = 0;
                }
                if (right && right->num >= T->t && richR)
                {
                    child->keys[child->num] = node->keys[idx];
                    child->childrens[child->num + 1] = right->childrens[0];
                    child->num++;

                    node->keys[idx] = right->keys[0];
                    for (i = 0;i < right->num - 1;i++)
                    {
                        right->keys[i] = right->keys[i + 1];
                        right->childrens[i] = right->childrens[i + 1];
                    }
                    right->keys[right->num - 1] = 0;
                    right->childrens[right->num - 1] = right->childrens[right->num];
                    right->childrens[right->num] = NULL;
                    right->num--;
                }
                else
                /*如果 node->children 以及 node->children 的所有兄弟结点都只有 t-1 个关键字, 则把 node->children 与一个兄弟合
                并, 即把 node 的一个关键字移到新合并的结点, 使之成为该结点的中间关键字*/
                {
                    for (i = child->num;i > 0;i--)
                    {
                        child->keys[i] = child->keys[i - 1];
                        child->childrens[i + 1] = child->childrens[i];
                    }
                    child->childrens[1] = child->childrens[0];
                    child->childrens[0] = left->childrens[left->num];
                    child->keys[0] = node->keys[idx - 1];
                    child->num++;

                    left->keys[left->num - 1] = 0;
                    left->childrens[left->num] = NULL;
                    left->num--;
                }
            }
            else if ((!left || (left->num == T->t - 1)) && (!right || (right->num == T->t - 1)))
            {
                if (left && left->num == T->t - 1)
                {
                    BTree_Merge(T, node, idx - 1);
                    child = left;
                }
                else if (right && right->num == T->t - 1)
                {
                    BTree_Merge(T, node, idx);
                }
            }
        }
        BTree_DeleteKey(T, child, key);
    }

}

3.12 摧毁B树 Tree_Destroy

int BTree_DeleteTree(BTree* T, int key)   //删除B树
{
    if (!T->root)
        return -1;
    BTree_DeleteKey(T, T->root, key);
    return 0;
}

4.剪贴板字符处理模块 GETPALTE.h

4.0 前置声明

/**
 * Code by C
 * @version 1.40 2020-09-10
 * @author MPX
*/

#include<stdio.h>
#include<ctype.h>
#include<windows.h> //需要使用到系统提供的剪切板clipboard

#define N 2000

//自动读取剪贴板数据
int GetPlateString(FILE* fp);
//自动读取复制代码到剪贴板
int StringAutoCopy(FILE* fp);
//读取文件多行内容
char* GetFileString(int m, int n, char* mystring, FILE* fp);

4.1 自动读取剪贴板

int GetPlateString(FILE* fp)
{
	char* pbuf = NULL;
	int i, count = 0;//count 表示单词的数量

	HANDLE hclip;//剪切板句柄

	//打开剪切板,获取里面的数据

	if (OpenClipboard(NULL) == 0)
	{
		printf("fail to open the Plate\n");
		return -1;
	}

	//剪切板当中的数据是不是文本类型的
	if (!IsClipboardFormatAvailable(CF_TEXT))
	{
		printf("剪切板当中的数据类型不匹配!\n");
		CloseClipboard();
		return -1;
	}

	//获取剪切板里面的数据
	hclip = GetClipboardData(CF_TEXT);
	pbuf = (char*)GlobalLock(hclip);  //加锁,返回一个VOID类型的指针
	GlobalUnlock(hclip);  //解锁

	fprintf(fp, "%s", pbuf);
	printf("%s", pbuf);

	CloseClipboard();//关闭剪切板,不然其他程序无法正常0使用剪切板

	return 0;
}

4.2 自动复制代码

int StringAutoCopy(FILE* fp)
{
    if (!OpenClipboard(NULL) || !EmptyClipboard())
    {
        printf("打开或清空剪切板出错!\n");
        return;
    }

    HGLOBAL hMen;
    char strText[10000];

    GetFileString(0, 2000, strText, fp);

    // 分配全局内存    
    hMen = GlobalAlloc(GMEM_MOVEABLE, ((strlen(strText) + 1) * sizeof(char)));

    if (!hMen)
    {
        printf("分配全局内存出错!\n");
        // 关闭剪切板    
        CloseClipboard();
        return;
    }

    // 把数据拷贝考全局内存中   
    // 锁住内存区    
    LPSTR lpStr = (LPSTR)GlobalLock(hMen);

    // 内存复制   
    memcpy(lpStr, strText, ((strlen(strText)) * sizeof(char)));
    // 字符结束符    
    lpStr[strlen(strText)] = (TCHAR)0;
    // 释放锁    
    GlobalUnlock(hMen);

    // 把内存中的数据放到剪切板上   
    SetClipboardData(CF_TEXT, hMen);
    CloseClipboard();
    printf("复制成功!");

	return 0;
}

4.3 一次性读取文件多行内容

char* GetFileString(int m, int n, char* mystring, FILE* fp) {
    int i;
    char str[N];
    *mystring = '\0';

    fseek(fp, 0L, SEEK_SET);
    for (i = 1; i < m; i++) {
        if (fgets(str, N, fp) == NULL) {
            //printf("文件长度不足");
            fclose(fp);
            return NULL;
        }
    }

    for (i = 1; i < n; i++) {
        if (fgets(str, N, fp) == NULL) {
            //printf("文件长度不足");
            fclose(fp);
            return NULL;
        }
        strcat_s(mystring, strlen(mystring) + strlen(str) + 1, str);
    }
    fclose(fp);
    return mystring;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值