需要知道的一些知识,我放在这个系列的上集中了
准备工作
模拟一个小型数据库系统(DBMS)的实现。
目的:实现数据库,数据表和数据的增加,查询,修改和删除的简单操作。
其中指令语句是自己想的适配于自己代码的类似SQL语言,所学有限,以此记录给自己日后回看嘻嘻,如有不足,欢迎指出,希望相互学习,不要直接copy(当然你copy也没用,关键运行步骤我删掉啦哈哈哈哈)。
一、 主要功能模块
(1)class Table类(专注于对某个数据表的操作)
- 插入信息
- 查询信息
- 删除信息
- 修改信息
(2) class Database类(专注对不同数据表的操作)
- 选择某个数据表进行细致操作
- 显示目前数据库中所有数据表
- 创建数据表
- 删除数据表
二、具体板块及其实现思路
Table.h
由上诉的设定中我们可以知道,在Table类中我们将完成对某个特定数据表的一系列操作。
#define Max 100
//设置根目录,用于后续操作找到需要进行操作的文件
const string root_path="D:\\mysql";
vector<string>table_list;
class Table{
private:
string table_name;
string path;
int number;//数据表中属性的个数
char source[Max];
bool Is_index=0;
public:
Table();
void initialize();//初始化
void insert_data(const char *source);//插入信息
void select_data(char *source);//查询信息
void update_data(const char *source);//更新信息
void delete_data(const char *source); //删除一条信息
};
① 初始化函数initialize()
:
初始化界面,也是为了提示自己需要输入的类似sql语句的格式:
void Table::initialize(){//初始化时用于提示输入信息
printf("hello!\n");
printf("welcome to the data-base made by:KQxixi\n");
printf("now please use the sql like:\n");
printf(" (插入):insert into table values(...;...;...;)\n");
printf(" (删除):delete xxx=xxx from table\n");
printf(" (查询):select xxx=xxx from table\n");
printf(" (修改):update data from xxx into xxx where xxx=xxx\n");
printf("(退出对数据表的操作):over\n");
}
② 插入信息函数insert_data(const char *source)
:
关键使用库函数strstr匹配用户输入串关键词的位置(比如:insert…values),然后将各个属性值的字符串先存入数组,然后将各个字符串顺序插入到文件中。
void Table::insert_data(const char *source){
char *temp=strstr(source,"values");
temp=temp+6;//使指针移到values子句开始
char now[Max];
int pivot=0;
string temp_path1=path+".txt";
string temp_path2=path+"_insert.txt";
fstream out;//建立流对象
char filename2[Max];
strcpy(filename2,temp_path2.c_str());
out.open(filename2,ios::out|ios::app);
char filename1[Max];
strcpy(filename1,temp_path1.c_str());
ifstream in(filename);//先读取
string line;
if (! in.is_open()){
cout << "Error opening file !!"; exit (1);
}
while (getline(in,line)){//读取文件中的每行记录
out<<line<<endl;
}
in.close();
now[0]='\0';
for(temp=temp+1;*temp!=')';temp++){
if(*temp==';'){
now[pivot]='\0';//增加串结尾符
out <<now<<endl; //插入到文件中
number++;//属性值加1
pivot=0;
}
else
now[pivot++]=*temp;
}
printf("insert data successfully!\n\n");
out.close();//保存文件
}
③ 查询函数select_data(char *source)
:
查找记录的大概思路也很简单,先记录输入指令中的关键信息,然后顺序读取指定文件中的每条记录,若该记录符合则直接输出并保存在新的文件中便于用户端查看(留下记录),否则跳过,读下一条记录。
不足:由于主要使用strstr()函数,如果想查询AGE=2的数据信息,AGE=22的数据也会输出(让我再学学看怎么解决)
void Table::select_data(char *source){
clock_t start,finish;//建立对象
double totaltime;
start=clock();//get current time of cpu
char *temp=source;
temp+=6;//来到select子句开始
//select ID=1 from table
for(;*temp!='=';temp++);
temp++;//移动temp直到=
char sym[Max];
int subscript=0;
for(;*temp!=' ';temp++)
sym[subscript++]=*temp;
sym[subscript]='\0';
string now;
fstream out;//建立流对象
string temp_path1=path+".txt";
string temp_path2=path+"_select.txt";
char filename2[Max];
strcpy(filename2,temp_path2.c_str());
char filename1[Max];
strcpy(filename1,temp_path1.c_str());
out.open(filename2,ios::out|ios::app);
ifstream in(filename);//先读取
if (! in.is_open()){
cout << "Error opening file !!!";
exit (1);
}
while(getline(in,now)){//读取文件中的每行记录
char check[Max];
subscript=0;
strcpy(check,now.c_str());//c_str()将string类型转化为char *类型
if(strstr(check,sym)!=NULL){
cout<<check<<endl;
out<<check<<endl;
}
}
in.close();
out.close();
finish=clock();//现在的时间
totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
//现在的时间-设置的初始时间=程序运行的时间,转化为s
printf("Option done successfully! and the Runtime is: %lf s\n",totaltime);
}
④更新数据函数update_data(const char *source)
:
修改记录的思路:先匹配where后的判断条件所在的位置,同时记录要修改的属性项和要修改的值。举个例子: update data from 23 into 13 where NAME=sunshine
我们要匹配的字符串就是NAME=sunshine,找到叫sunshine的人的信息所在(这个时候可能存在同名的情况),就再判断这个人的年龄是不是23,如果是,则将23改为13(当然你要是问我如果同名同岁,emmm那具体情况的时候再加其他判断吧)。
void Table::update_data(const char *source){ //更新操作
char *temp=strstr(source,"from");
int pivot=0;
char obvious[Max],aim[Max],address[Max];
for(temp=temp+5;*temp!=' ';temp++)//匹配要设置的属性项
obvious[pivot++]=*temp;
obvious[pivot]='\0';
pivot=0;
temp=strstr(source,"into");
for(temp=temp+5;*temp!=' ';temp++)//匹配要设置的属性项
aim[pivot++]=*temp;
aim[pivot]='\0';
temp=strstr(source,"=");
pivot=0;
for(temp=temp+1;*temp!='\0';temp++) //匹配要设置的属性项的值
address[pivot++]=*temp;
address[pivot]='\0';
pivot=0;
string temp_path1=path+".txt";
string temp_path2=path+"_update.txt";
char filename2[Max];
strcpy(filename2,temp_path2.c_str());
char filename1[Max];
strcpy(filename1,temp_path1.c_str());
ifstream in(filename);//输入流对象,关联文件
fstream out;
out.open(filename,ios::out);//关联文件
string line;
while(getline(in,line)){
char check[Max];
strcpy(check,line.c_str());
strcat(check,"\0");
if(strstr(check,address)!=NULL){
temp=strstr(check,obvious);
if(temp!=NULL){
pivot=0;
for(;*temp!=' '&&aim[pivot]!='\0';temp++,pivot++){
*temp=aim[pivot];
}
char temperary[Max];
char *k;
int flag=0;
if(aim[pivot]!='\0'){
flag=1;
strcpy(temperary,temp);
strcat(temperary,"\0");
}
while(aim[pivot]!='\0'&&flag==1){
*temp=aim[pivot];
pivot++;
temp++;
}
*temp='\0';
strcat(temp,temperary);
if(*temp!=' '&&flag==0){
k=temp;
for(;*k!=' '&&*k!='\0';k++);
if(*k==' '){
strcpy(temperary,k);
*temp='\0';
strcat(temp,temperary);
}
if(*k=='\0')
*temp='\0';
}
}
}
out<<check<<endl;
}
in.close();
out.close();
printf("update data successfully!\n\n");
}
⑤删除信息函数delete_data(const char *source)
:
删除函数的思路:首先也是匹配要删除判别的属性项及其值。接着就是顺序扫描文件,读取文件中的每条记录,判段相应的属性项的值是否满足删除条件,若满足则直接判断下一条记录,否则将该记录写入到辅助文件中,这样,辅助文件存放的就是最终删除后的各记录值。
void Table::delete_data(const char *source){
char *temp=strstr(source,"=");
//使指针移到delete子句开始
char now[Max];
int pivot=0;
for(temp=temp+1;*temp!=' ';temp++)
now[pivot++]=*temp;
now[pivot]='\0';
string temp_path1=path+".txt";
string temp_path2=path+"_delete.txt";
fstream out;//建立流对象
char filename2[Max];
strcpy(filename2,temp_path2.c_str());
char filename1[Max];
strcpy(filename1,temp_path1.c_str());
out.open(filename2,ios::out|ios::app);
ifstream in(filename);//先读取
string line;
if (! in.is_open()){
cout << "Error opening file !!!!!"; exit (1);
}
while (getline(in,line)){//读取文件中的每行记录
char check[Max];
strcpy(check,line.c_str());//c_str()将string类型转化为char *类型
if(strstr(check,now)==0){
out<<check<<endl;
}
}
in.close();
printf("delete data successfully!\n\n");
out.close();//保存文件
}
⑥构造函数Table()
我们知道在c++中,类内构造函数和析构函数是自动调用的
好好利用构造函数其实是因为我把主流程设定在了其中,然后自动匹配用户输入的字符串,判断匹配的字符串是要执行哪部分功能,然后调用相应类的成员函数去执行。
Table::Table(){//构造函数
printf("请确认要进行操作的数据表的名称:");
cin>>table_name;
path=root_path+table_name;
initialize();//初始化
while(gets(source)){//输入决定调用相应的功能
if(strstr(source,"insert")!=NULL)//调用相应的插入处理函数
insert_data(source);
else if(strstr(source,"delete")!=NULL)//调用删除处理函数
delete_data(source);
else if(strstr(source,"select")!=NULL && !Is_index)//调用查询函数
select_data(source);
else if(strstr(source,"update")!=NULL) //调用修改函数
update_data(source);
else if(strstr(source,"over")!=NULL)//结束操作
break;
else
printf("格式错误!\n");
}
}
以上,Table类我们就完成啦
Database.h
时间有限,Database类的实现我就水了很多,程序这个东西,越用心分支越多你赋予它的功能就越强大,所以用心打磨一个东西的精神一直都值得被推崇呀!
注意:
Database类实现对该数据库中所有数据表的简单操作,同理其实我们也可以推出,我们也可以设置一个类用于管理不同的数据库。
#include"Table.h"
string table_name
class Database{
private:
//当前所使用数据库的存储路径
const string data_path="D:\\mysql\\Database\\";
int number;
public:
string temp_name;
//数据库中的数据表信息
vector<string> table_list;
Database();
//选择数据表(use database)
void use_table(string table_name);
//显示所有数据表(show tables)
void show();
//创建数据表 (create table)
void create_table(const char *source);
//删除数据表 (drop table)
void drop_table(string table_name);
};
①选择一个数据表use_table(string table_name)
这段代码最关键的我觉得就是Table a;同时数据表的名字是前面设置文件路径的关键,通过数据表的名字(相当于你掌握了打开数据表的钥匙)就可以找到对应文件进行数据表的操作(因为可以调用Table类中的成员函数了呀!)
void Database::use_table(string table_name){
//判断数据表是否存在,若存在则开始操作
int flag=0;
vector<string>::iterator iter;
for(iter=table_list.begin();iter!=table_list.end();iter++){
if(*(iter)==table_name){
table_list.erase(iter);
flag=1;
break;
}
}
if(flag==0){
cout<<"can not open the file! The name input may be wrong."<<endl;
return;
}
else{
cout<<"操作已就绪!"<<endl;
Table a;
cout<<"Database changed"<<endl;
}
}
②展示当前数据库中所有的数据表名称show()
emmm我jio得就是字面意思
void Database::show(){
if(table_list.size()!=0){
for(int i=0;i<table_list.size();i++)
cout<<i+1<<"."<<table_list[i]<<endl;
}
else
printf("数据库中暂无信息!\n");
}
③在当前数据库下新建数据表create_table(const char *source)
建表相对而言较为简单,当用户输入”Create”建表语句时,则调用函数进行建表,该函数的功能最后会建立一个文本文档。
void Database::create_table(const char *source){
fstream out;//建立流对象
string temp_path=data_path+temp_name+".txt";
char filename[Max];
strcpy(filename,temp_path.c_str());
out.open(filename,ios::out);
char *temp=0;
int line=1;
int num=0;
char table[Max][Max];//记录表的各个属性
temp=strstr(source,"(");
for(temp=temp+1;*temp!=')';temp++){
if(*temp==';'){
//每一个分号隔开的数据都是一个属性值,此时选择存入关系的二维表中
table[line][num]='\0';
num=0;
out<<table[line]<<endl;
line++;
}
else
table[line][num++]=*temp;
}
number=line;//关系的度
printf("Table created successfully!\n");
out.close();
}
④删除数据表drop_table(string table_name
我的想法是:Database类实现对Table类的掌控是通过掌控对应数据表的名字实现的,所以我的设定中想的是只要从数据库中删掉数据表的名称,就实现了数据表的删除(尽管这样是在自欺欺人,数据没有删除,相应存储空间没有释放,啊!我要继续学习)
void Database::drop_table(string table_name){
//判断数据表是否存在,若存在则从table_list中删除
int flag=0;
vector<string>::iterator it;
for(it=table_list.begin();it!=table_list.end();it++){
if(*(it)==table_name){
table_list.erase(it);
flag=1;
break;
}
}
if(!flag){
cout<<"Can't drop table '"<<table_name<<"'; table doesn't exist"<<endl;
return;
}
cout<<"delete table!"<<endl;
}
⑤构造函数
道理和Table类差不多
Database::Database(){
int choose;
int flag=0;
printf("数据库已就绪!\n");
printf("welcome to the data-base made by:KQxixi\n");
printf("now please choose the function you like:\n");
printf(" 0:退出操作\n") ;
printf(" 1:显示数据库中所有数据表的名称\n");
printf(" 2.建表\n");
printf(" 3.删表\n");
printf(" 4.选择某个表进行操作,eg:插入,查询,删除等\n");
cout<<"请选择:";
cin>>choose;
while(choose!=0){
if(choose==1)
show();
if(choose==2){
printf("请输入要新建的数据表的名称:");
cin>>table_name;
//判断是否新建数据表重名
for(int i=0;i<table_list.size();i++){
if(table_list[i]==table_name){
flag=1;
cout<<"Can't create table '"<<table_name<<"'; table exists"<<endl;
}
}
if(flag!=1){
table_list.push_back(table_name);
temp_name=table_name;
printf("(建表输入语句请如):create table(...;...;...;)\n");
char source[Max];
getchar();
gets(source);
create_table(source);
}
}
if(choose==3){
printf("请输入要删除的数据表的名称:");
cin>>table_name;
drop_table(table_name);
}
if(choose==4){
printf("请输入要进行操作的数据表的名称:");
cin>>table_name;
use_table(table_name);
}
cout<<endl;
cout<<"请输入相应功能前的序号值:";
cin>>choose;
}
}
main.cpp
写到这里,我最喜欢main
#include<iostream>
#include<stdio.h>
#include"Database.h"
using namespace std;
int main(void){
printf("HELLO!");
Database database;
return 0;
}
代码实现结果:
咳咳总结陈词:诸多不足,有望进步,冲冲冲!