zoukankan      html  css  js  c++  java
  • C++程设实验项目二:用正则表达式制作一个简易的SQL系统

    本文将尽可能简单地概括如何搭起这个SQL系统的框架。

    一、正则表达式分析语句

    首先需要使用c++的regex库:

    #include <regex>

    推荐到菜鸟教程上了解正则表达式的最基础语法。

    然后,新建一个表达式。假定现在要分析的语句是CREATE TABLE (col1,col2,...) TO filename

    regex r("CREATE TABLE \((.+)\) TO ([^ ]+)");

    注意,在c++中还要转义一次反斜杠,所以一般的像w, (这样的符号都要写成\w, \(的形式。

    然后用regex_match匹配一个string对象。

    string cmd="CREATE TABLE (name,score) TO rec.txt";
    smatch m;
    if(regex_match(cmd,m,r)){
        //...
    }
    else cout << "NOT MATCH!" << endl;

    如果cmd的内容与r匹配成功,那么结果将会保存在m里面。

    那么怎么利用m的内容呢?举个例子,输出m的内容(for里面的auto这个用法也是c++11才有的)

    for(auto x:m) cout << m << endl;
    //result:
    //CREATE TABLE (name,score) TO rec.txt     //即m.str(0),匹配到的整个式子
    //name,score                               //即m.str(1),即用n个括号保存的信息,都依次保存在m的1~n个位置中//rec.txt                                  //即m.str(2)

    将m.str(1)保存之后,如何处理它呢?万一这是一个很长的col1,col2,col3,...,col99的长字符串呢?这就要用到regex_search了。

    regex c("([^,]+)");
    string cols=m.str(1);
    while(regex_search(cols,m,c)){ //确保m的文件路径信息已经被保存了
        //保存m.str(0)的信息……
        cols=m.suffix().str();  
        //m.str(0): col1
        //m.suffix(): ,col2,col3,...,col99
    }

    regex_search会在整个字符串中寻找第一个匹配正则表达式的字串。然后,这个字串之前的部分保存在m.prefix(),之后的部分保存在m.suffix()。只要把已经查找到的字串截去,就可以再在后面的串里搜索了。

    再举个例子吧:

    regex r("glim");
    string s="starlightglimmer";
    smatch m;
    regex_search(s,m,r);
    //m.str(0)=="glim"
    //m.prefix()=="starlight"
    //m.suffix()=="mer"

    由此,只要灵活地运用正则表达式,就可以很轻松地分析各种语句了。即使是有多个可选的命令,例如SELECT * FROM table [WHERE col = name] [ORDER BY col DESC] [TO file],你可以将前面的必填命令和后面三个可选参数拆成四个正则表达式,然后运用regex_search完成各种命令的分析。

    二、数据结构与排序

    注意,由于各人的数据结构存在差异,所以这部分不是这么通用。首先,一个表格是二维的,这就可以使用一个vector的vector来储存表格。具体我是这样操作的:

    struct column {
        vector<string> item;
        string name;
    };
    class table {
    public:
        vector<column> col;
        string tablename;
        table(string);
    };

    table其实当成结构体用,所以都public。

    一个表的行数可以从它其中一个列里得知,同时你可以要求表名与文件名相同来节省一个string变量。

    构造函数的string就是方便设置tablename用的,你也可以把它拆出来,然后写成一个结构体。

    此外,在内存里,你不必储存任何一个静态的表格:毕竟是从文件读取的,只要确保要操作的表格曾经通过CREATE TABLE记录在一个vector<string> tablelist这样的地方就行了。

    那么,如何对一个行操作呢?平常可以用下标来操作,但是排序怎么解决?这里提供一个思路:

    例如,存在这些列:name, score, note

    要对score排序整个表,可以用pair<int, string>这样的数据结构。

    //table t(...)
    //...
    string tarcol="score";
    vector<pair<int ,string>> tar;
    for(auto c:t.col){
        if(c.name==tarcol){
            for(int i=0;i<c.item.size();i++){
                tar.push_back(make_pair(i,c.item[i]));
            }
        }
    }

    然后用<algorithm>自带的sort,同时配以自定义的cmp函数:

    bool cmp(const pair &a, const pair &b) {
        int res = a.second.compare(b.second);
        if (res < 0) return true;
        else return false;
    }

    那么你就得到了一个排序过的pair序列。pair的数字,就是一种索引。

    把当前的表一行一行地,根据索引指定的顺序塞进一个临时的新表(比如,序列的第一个索引数字是2,就把第二行的内容塞进表)。

    最后,把新表复制会源表,就大功告成了。

    三、文件的读取

    可以考虑配合fstream,先用getline,再用流输入。具体操作:

    #include <fstream>
    int readtable(table &t) {
        ifstream in;
        char a[300];
        string line;
        column tmpc;
    
        in.open((t.tablename + ".txt").c_str());
        if (!in.is_open()) {
            //printf("Doc is not exist...
    ");
            return -1;
        }
        in.getline(a, 299);
        line = a;
    
        smatch m;
        while (regex_search(line, m, divideSpace)) {
            tmpc.name = m.str(1);
            t.col.push_back(tmpc);
            line = m.suffix().str();
        }
    
        while (!in.eof()) {
            for (auto &c : t.col) {
                in >> line;
                c.item.push_back(line);
            }
        }
        for (auto &c : t.col) c.item.pop_back(); //delete invalid line
        in.close();
        return 1;
    }

    要注意的是,一般读取的时候是会多读一行的,这时候要把这多读的一行移除。此外,正则表达式就留给读者自己写了。

    输出自然不难了。通过这种方法,可以每次都从文件读取表,然后再储存表,再退出。

    四、表格分割线的绘制

    这个其实不是难点,毕竟只要用一个vector<int>记录下一列的最长字串的长度,就可以轻松绘制了。

    但是这里有一个坑:在Linux下,一个汉字占三个长度,然后实际显示只有两个。所以在计算长度的时候,需要进行一些处理。

    具体来说,可以这么取巧:

    int calLength(string s){
        double len=0;
        for(auto ch:s){
            if(0<=ch||ch<=127) len+=1;
            else len+=2.0/3;
        }
        return (int)len;
    }

    以上。感谢阅读。

  • 相关阅读:
    jQuery动画速成
    elementui更改导航栏样式
    Python反射
    Linux基础--基本命令
    Linux基础--简介
    DRF
    Socket与Websocket初识
    五星宏辉游戏项目小结
    Redis详解(8)--redis-cluster
    Redis详解(7)--主从复制
  • 原文地址:https://www.cnblogs.com/KakagouLT/p/9032930.html
Copyright © 2011-2022 走看看