锵锵锵~数据库的模拟实现

需要知道的一些知识,我放在这个系列的上集中了
准备工作

模拟一个小型数据库系统(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;
} 

代码实现结果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

咳咳总结陈词:诸多不足,有望进步,冲冲冲!

猜你喜欢

转载自blog.csdn.net/KQwangxi/article/details/106948408