[debug] dbms lab (3) sql语言解释器模块——正则表达式、字符串删除收尾空格

Contents

    • C++正则表达式的使用
      • 试验一:嵌套结构的匹配(多层括号试验)
      • 试验二:重复结构的匹配(多节匹配)
    • 程序架构
      • 整体结构
      • 命名空间
      • 宏保护
      • 头文件代码
    • 实现
      • 分流函数
      • 删除字符串首尾的空格
      • 语句的解释——正则表达式实例和字符串处理
        • create语句实现
        • use语句实现
        • insert语句实现
        • where子句解释
        • select语句实现
        • update语句实现
        • delete语句实现

C++正则表达式的使用

为了使用正则表达式,我们或许尝试着全盘学习,
教程可见:菜鸟教程:学的不仅是技术,更是梦想!

大量的内容并不好用。我们从实用的角度,进行了如下的两个试验:

试验一:嵌套结构的匹配(多层括号试验)

    regex pattern("^.*?[(](([A-Za-z0-9_()]+),)+[)]");
    smatch tmp;
    string src(", (fh,)");
    regex_match(src, tmp, pattern);
    for (auto it = tmp.begin(); it < tmp.end(); it++)
        cout << *it << endl;
    return 0;

试验输出:

, (fh,)
fh,
fh

这个试验告诉我们,C++可以进行多层小括号的匹配。

试验二:重复结构的匹配(多节匹配)

	regex pattern("^.*?[(]((([A-Za-z0-9_()]+),){1,})[)]");
    smatch tmp;
    string src(", (asd,fh,)");
    regex_match(src, tmp, pattern);
    for (auto it = tmp.begin(); it < tmp.end(); it++)
        cout << *it << endl;
    return 0;

输出结果:这个试验告诉我们,C++正则,尽管可以利用次数限制,使得表达式完成匹配,但重复匹配的部分仅保留最后一次结果,所以在建立表格的时候,我们不能利用这样的语法实现任意列数的表格。

, (asd,fh,)
asd,fh,
fh,
fh

程序架构

整体结构

Interpreter是一个通用接口,接受Input函数带来的以非空字符开始、分号结尾的字符串。然后利用switch的语句进行分流。这个简易程序支持以下操作:

create database
create table
use
insert
update
delete
where

命名空间

由于同样的几个操作也在流操作文件中出现,为了避免重名,也为了避免冗长的命名,使用命名空间实在是一个非常好的选择。

几个要点

  • 命名空间中的变量或者函数在使用过程当中必须使用作用域标识符。虽然看起来也很冗长,但我心里清楚这是一种很明朗的表达方式:
    1. 它既用简单的标识符表明了这个量干什么用(这种意义上的同名可以让我们很方便的把同功能的函数归在一块),更重要的是定义了——
    2. 从哪来(定义在哪里)。

宏保护

  • 宏保护可以让我们更专注于文件架构本身,而不用对缠绕的交叉引用焦头烂额
  • 由于交叉链接的关系,Clause的重复定义导致了程序难以通过,我们专门为这个结构体增加了一个宏保护。

头文件代码

程序如下:

//interpreter.hpp
/**
 * @file: interpreter.hpp
 * @author: fhn
 * @date: 4/20
 * @version: 1.0 to be improved : a function which formats a string into pieces of strings without spaces in them 
 * @description: receiving params from the ui::Input(), 
 *                  containing the main parts of interpreters
 *                  in different functions.
*/

//--------------------------------------------------------------------------------------------

#ifndef INTERPRETER_GUARD
#define INTERPRETER_GUARD

#include 
#include 
#include 
#include 
#include 
#include 
#include "in_out.hpp"

//--------------------------------------------------------------------------------------------


void Interpret(const std::string&);
#ifndef CLAUSEGUARD
#define CLAUSEGUARD
struct Clause
{
    std::string name;
    std::string op;
    std::string value;
    Clause(std::string a, std::string b, std::string c) :name(a), op(b), value(c){ }; 
    Clause() : name(), op(), value(){ }
};
#endif

/**
 * @author: fhn
 * @date: 4/21
 * @description: a namespace targeted for Interpret();
 * @version: 
*/
namespace sql_itp{
using std::string;

void trim(string&);
//free of where

/**
 * @author: fhn
 * @date: 4/22
 * @description: call in_out::Create(database_name) to create a database(as a filefolder) or table(file)
*/
void Create(char mode, const string& src);

/**
 * @author: fhn
 * @date: 4/22
 * @description: call in_out::Use(database_name), impl by changing cur_db.
*/
void Use(const string& src);

/**
* @author: fhn
* @date: 4/22
* @description: call in_out::Insert(table_name, )
*/
void Insert(const string& src);


//fucking blessed with where clause

/**
* @author: fhn
* @date: 4/25
* @description: return a Clause obj including info of Where.
*/
Clause Where(const string& src);

void Select(const string& src);
void Update(const string& src);
void Delete(const string& src);

} // namespace sql_itp


#endif

实现

分流函数

其实常引用并无,必要毕竟产生了拷贝开销。
最后的流清除操作可以在异常部分进行。

void Interpret(const std::string& raw)
{
    using std::string; using std::stringstream;
    string judge;
    stringstream ss(raw);
    ss >> judge;
    switch(judge[0])
    {

        case 'c': case 'C':
            ss >> judge;
            sql_itp::Create(judge[0], raw);
            break;
        case 'i': case 'I':
            ss >> judge;
            sql_itp::Insert(raw);
            break;
        case 'u': case 'U':
            if (judge[1] == 's' || judge[1] == 'S')
                sql_itp::Use(raw);
            else sql_itp::Update(raw);
            break;
        case 's': case 'S':
            sql_itp::Select(raw);
            break;
        case 'd': case 'D':
            sql_itp::Delete(raw);
            break;
        error("sentence syntax went wrong."); fflush(stdin);
    }
#ifdef _LOC_
std::clog << "finished once" << std::endl;
#endif
    fflush(stdin);
}

删除字符串首尾的空格

由于传入的raw字符串可能存在非法的空格,我们增加了如下的函数

void trim(string & src)
{
    src.erase(0, src.find_first_not_of(" "));
    src.erase(src.find_last_not_of(" ")+1);
}

这个函数是STL中find算法和string字符串处理的一个好例子。

语句的解释——正则表达式实例和字符串处理

发生语句解释,并提取有效信息传递给对应的I/O函数。

create语句实现

要点:

  • 每个分支都要有异常判别:总体分支(database or table),table数据类型(int or varchar)
  • 读入“变量名-类型”结构的时候,这个字符串处理的结构有讨论空间: peek()函数的使用帮助我们免去计算的麻烦,便利地实现忽略前导无效字符的操作。compare()辅助确定具体类型。
  • 还是要注意我们先前做过的正则表达式试验,mat中保存的第一个变量是识别好的整句。icase是选项,表示忽略大小写
  • 传入的表信息利用in_out.hpp中定义的col_info结构传递。
void Create(char mode, const string& src)
{
    if (mode == 'd' || mode == 'D')
    {
        std::regex pattern{"^.*?create.*?database.*?(\\w+).*?;$", std::regex::icase};
        std::smatch mat;
        if (!regex_match(src, mat, pattern))
        { error("create database : syntax wrong"); return; }
        in_out::CreateDatabase(mat[1]);
    }
    else if (mode == 't' || mode == 'T')// 
    {
        std::regex pattern{"^.*?create.*?Table.*?(\\w+)\\s*[(]\\s*(.*)[)].*?;.*?$", std::regex::icase};
                                //table name must be in the alphas, nums and '_'
        std::smatch mat;
        if (!regex_match(src, mat, pattern))
        { error("create table : syntax wrong"); return;}//mat[1] is tablename, mat[2] is the param of columns.
    #ifdef _LOC_
        else 
    std::clog << "create table matched" << std::endl;
    #endif
        in_out::ColInfo param;
        std::string name, type;
        std::stringstream ss(mat[2]);
        while (ss.peek() != EOF) // if using "while (ss)", the eof won't be trigered in time.
        {
            while (ss.peek() == ' ') ss.ignore(); 
            getline(ss, name, ' ');
            while (ss.peek() == ' ') ss.ignore();// ignore the heading spaces.
            getline(ss, type, ',');
            trim(name); trim(type);
            if (type[0] == 'i' || type[0] == 'I') 
            {    
                if (!type.compare(0,3, "int", 0,3) && !type.compare(0,3, "INT", 0, 3))
                { error("var table type illegal"); return;}
                else param.AddCol(name, 0);
            }
            else if (type[0] == 'v' || type[0] == 'V') //this may be improved : in the form of 'varchar (20)' isn;t supported
                                    //2.1 : now supported.
            {
                int cnt = 7, ans = 0;
                if (!(type.compare(0, 7, "varchar", 0, 7)||type.compare(0, 7, "VARCHAR", 0, 7)))
                { error("var table type illegal"); return;}
                for (; cnt < type.size() && (!std::isdigit(type[cnt])); cnt++);
                if (cnt != type.size())
                    while (std::isdigit(type[cnt]))
                        ans = ans * 10 + (type[cnt]-'0'), cnt++;
                else { error("var table type illegal"); return;}
                param.AddCol(name, ans);
            }
            else 
            { error("var table type illegal"); return;}
        }
#ifdef _LOC_
std::clog << "create table prepared" << std::endl << std::endl;
#endif
        in_out::CreateTable(mat[1], param);
    }
    else error("#ERROR: create table mode wrong.");
}

use语句实现

void Use(const string& src)
{
    std::regex pattern{"^.*?use.*?(\\w+).*?;", std::regex::icase};
    std::smatch mat;
    if (!regex_match(src, mat, pattern))
    { error("use sentence wrong"); return; };
    string tmp = mat[1].str();
    trim(tmp);
    in_out::Use(tmp);
}

insert语句实现

一次插入一行,所有数据都用string表示,传出使用vector

void Insert(const string& src)
{
    std::regex pattern{"^.*?insert.*?into.*?(\\w+).*?value.*?[(]\\s*(.*)[)].*?;.*?$", std::regex::icase};
    std::smatch mat;
    if (!regex_match(src, mat, pattern))
    { error("insert syntax wrong: whole sentence"); return; }
    std::string name(mat[1]);
    std::stringstream ss(mat[2]);
    std::vector<string> vec_param;
    std::string tmp;
    while (ss.peek() != EOF)
    {
        while (ss.peek() == ' ') ss.ignore();
        getline(ss, tmp, ',');
        trim(tmp);
        vec_param.push_back(tmp);
    }
    in_out::Insert(name, vec_param);
}

where子句解释

以下的三个语句都要一定一定程度上依赖where子句的解释,所以,我们先给出where子句的结构

  • 这里要特别考虑符号的处理
/**
 * @author: fhn
 * @date: 4/25
 * @description: transform a string of 'where clause' into a Clause obj(which contains the info of it).
 * @input: a string without front and rear spaces; started with where. 
 *          if a void string in, directly return an obj (0, 0, 0)
 * @version: 1.0
 *          v1.1: the * and + cause the regex to go wrong, improve the intercommunication of regex function cut the ';'
*/
Clause Where(const string& src)
{
    std::regex pattern{"^.*?where\\s+(\\w+)\\s*([><=!]{1,2})\\s*(.+?)\\s*$", std::regex::icase};
    std::smatch mat;
    if (regex_match(src, mat, pattern))
        return Clause{mat[1].str(), mat[2].str(), mat[3].str()};
    return Clause();
}

select语句实现

select同样也需要

void Select(const string& src)
{
    std::regex pattern{"^.*?select\\s*(.*)\\s*from\\s+(\\w+)\\s*(where.*?)*\\s*;.*?$", std::regex::icase};
    std::smatch mat;
    if (!regex_match(src, mat, pattern))
    { error("select syntax wrong: whole sentence"); return;};
    std::string name(mat[2]);
    std::stringstream ss(mat[1]);
    std::vector<string> vec_param;
    std::string tmp;
    while (ss.peek() != EOF)
    {
        getline(ss, tmp, ',');
        trim(tmp);
        vec_param.push_back(tmp);
    }
    in_out::Select(name, vec_param, Where(mat[3].str()));
}

update语句实现

void Update(const string& src)
{
    std::regex pattern{"^.*?update\\s+(\\w+)\\s+set\\s+(.*?)\\s*=\\s*(.+?)\\s+(where.*?)\\s*;.*?$", std::regex::icase};
    std::smatch mat;
    if (!regex_match(src, mat, pattern))
    { error("#ERROR: update syntax wrong: whole sentence"); return;};
    in_out::Update(mat[1].str(), mat[2].str(), mat[3].str(), Where(mat[4].str()));
}

delete语句实现

void Delete(const string& src)
{
    std::regex pattern{"^.*?delete\\s+from\\s+(\\w+)\\s+(where.*?)\\s*;.*?$", std::regex::icase};
    std::smatch mat;
    if (!regex_match(src, mat, pattern))
    { error("delete sentence wrong"); return; };
    in_out::Delete(mat[1].str(), Where(mat[2].str()));
}

你可能感兴趣的:(C/C++)