数据库(2):配置数据的缓存建立方法,建立通用表模型

来源:互联网 发布:淘宝个人第一大卖家 编辑:程序博客网 时间:2024/06/07 10:39

用于建立大量的数据表事务,建模过程无需关心数据数据表内容


例子: 一下是sqlite数据库的静态数据处理方案,改造一下可以成为静态和动态数据的缓存方案

/*! \file csvtable.h
    fileencoding:utf-8
    \brief 读取csv文件
    \author 
    \date
*/
#ifndef HANJJ_CSVTABLE_H
#define HANJJ_CSVTABLE_H

#include <string>
#include <map>
#include <vector>
#include <tr1/unordered_map>
#include "csv.h"

#define USE_SQLITE3 1

#ifdef USE_SQLITE3
#include <sqlite3.h>
#endif

//! 整型和整型的HashMap
typedef std::tr1::unordered_map< int, int > HashMap;
//! 整型和整型的HashMultiMap
typedef std::tr1::unordered_multimap< int, int > HashMultiMap;

//! @name 处理csv表的回调函数,表索引id唯一
//@{
//! 处理field,一个数据
void cb1(void* s, size_t len, void* data);
//! 处理row,遇到一行结束
void cb2(int c, void* data);
//@}

/* 用不用这里不知道,注释掉先
//! @name 处理csv表的回调函数,表索引id不唯一
//@{
//! 处理field,一个数据
void cb1m(void* s, size_t len, void* data);
//! 处理row,遇到一行结束
void cb2m(int c, void* data);
//@}
*/

//! 处理csv表的类
/*!
表结构如下
\code
+--------+------------+-------+
| keyid  | aux_keyid  | name1 |
+--------+------------+-------+
|  1     |     5      |  ok   |
+--------+------------+-------+
对于唯一id索引表,即keyid不重复,可以使用
std::string& getRec(int keyid, std::string& col_name) throw(std::string)
例如 string s = getRec(1, "name1"); // s="ok"
也可以使用
getColIndex和getRowIndex得到rowindex 和 colindex再使用
getRecFromIndex获得表值,注意检查index不可以超出范围(columns()和rows())
对于keyid不唯一的表,需要使用
getRowIndex获得rowindex
getColIndex获得colindex
再由getRecFromIndex得到需要的表值
同样注意检查index的范围!

\endcode

*/
class csvtable
{
public:
    csvtable();
    //! 初始化
    /*!
    \param filename 文件名
    \param table_type 表文件类型,0:具有唯一id索引的表 1:索引id不唯一,需要联合另外一个field来用
    \param fcb1 libcsv的cb1函数
    \param fcb2 libcsv的cb2函数
    \return 成功返回0 失败返回<0
    */
    int Init(const char* filename, int table_type = 0, void (*fcb1)(void *, size_t, void *) = cb1, void (*fcb2)(int, void *) = cb2);
    //! 检查col_index是否合法
    bool check_columns(size_t index)  const
    {
        if (index > (columns()-1)) return false;
        return true;
    }
    //! 检查row_index是否合法
    bool check_rows(size_t index)  const
    {
        if (index > (rows()-1)) return false;
        return true;
    }
    //! 按表keyid值和col_name得到表值,适用于唯一id索引表
    /*!
    当查表获得某个值时用这个,如果需要获得同一行的多个值,最好使用getRecFromIndex进行遍历而不是用这个函数遍历
    */
    const std::string& getRec(int keyid, const std::string& col_name) throw(std::string)
    {
        //  if (table_type != 0)  throw("use wrong function! table_type!=0 ");
        return getRec(keyid, getColIndex(col_name));
    }
    //! 按表关键字查col_index处的表值,只适用于唯一id索引表
    const std::string& getRec(int keyid, int col_index) throw (std::string)
    {
        if (table_type != 0)  throw("use wrong function! table_type!=0 ");
        if (check_columns(col_index)==false) throw(std::string("getRec check failed."));
        int ri = getRowIndex(keyid);
        //  if (check_rows(ri)==false) throw("getRrec check row failed");
        return (*((keylist[ri])+col_index));
    }

    //! 由行列index得到表值
    const std::string& getRecFromIndex(int RowIndex, int ColIndex)
    throw(std::string)
    {
        // 一般来讲,RowIndex不会出错,所以没有检查RowInex
        if (check_columns(ColIndex)==false)
            throw(std::string("getRecFromIndex check failed"));
        return (*(keylist[RowIndex]+ColIndex));
    }
    //! 由表头col_name得到col_index
    int getColIndex(const std::string& col_name) throw (std::string)
    {
        std::map< std::string, int >::iterator pos;
        pos = titlemap.find(col_name);
        if (pos != titlemap.end())
        {
            return pos->second;
        }
        throw(std::string("getColIndex not found:")+std::string(col_name));
    }
    //! 由keyid和aux_keyid两个表值确定rowindex
    /*!
    \param keyid 表关键索引id值
    \param aux_col_index 辅助列col的索引值,由getColIndex得到
    \param aux_keyid 辅助关键索引id值
        对应普通表,aux_keyid缺省为-1,此时只由keyid确定rowindex,对于aux_keyid不为-1的时候,从索引id不唯一的表中查找
    */
    int getRowIndex(int keyid, int aux_col_index = -1, int aux_keyid = -1) throw (std::string )
    {
        // 需要检查表类型和aux_keyid是否匹配
        // 普通表
        if (table_type ==0 && aux_keyid == -1)
        {
            HashMap::iterator pos;
            pos = keyindexmap.find(keyid);
            if (pos != keyindexmap.end())
            {
                return  pos->second;
            }
            throw(std::string("getRowIndex  failed"));
        }
        else if (table_type ==1 && aux_keyid > -1 && aux_col_index > -1)
        {
            std::pair<HashMultiMap::iterator, HashMultiMap::iterator> itpair =
                keyindexmultimap.equal_range(keyid);
            for (; itpair.first != itpair.second; ++itpair.first)
            {
                // itpair.first->second 为rowindex
                if (aux_keyid ==
                        atoi(getRecFromIndex(itpair.first->second
                                             , aux_col_index).c_str()))
                    return  itpair.first->second;

            }
            throw(std::string("getRowIndex 2 failed"));
        }
        // run here , there must something wrong.
        throw (std::string("table_type and aux_keyid not match or aux_keyid out of range!"));
    }

    //! 得到keyid的所有行索引
    /*!
    \param keyid 表关键索引id值
    \param vecIndex 返回所有行索引
    */
    int getRowSIndex(int keyid, std::vector<int>& vecIndex) throw (std::string)
    {
        // 需要检查表类型和aux_keyid是否匹配
        // 普通表
        vecIndex.clear();
        if (table_type == 0)
        {
            HashMap::iterator pos;
            pos = keyindexmap.find(keyid);
            if (pos != keyindexmap.end())
            {
                vecIndex.push_back(pos->second);
                return 1;
            }
            throw(std::string("getRowIndex  failed"));
            //  return 0;
        }
        else if (table_type == 1)
        {
            std::pair<HashMultiMap::iterator, HashMultiMap::iterator> itpair =
                keyindexmultimap.equal_range(keyid);

            if (itpair.first == itpair.second)
            {
                throw(std::string("getRowIndex 2 failed"));
            }
            for (; itpair.first != itpair.second; ++itpair.first)
            {
                vecIndex.push_back(itpair.first->second);
            }
            return vecIndex.size();
        }
        throw (std::string("table_type and aux_keyid not match or aux_keyid out of range!"));
    }

    //! 读取csv数据文件
    /*!
     * \param zSql 一條sql語句
     * sucess:0 error:<0
     */
    int read(const char* zSql=0);
    virtual ~csvtable();
    //! field计数
    void fields_count()
    {
        _fields++;
    }
    //! 返回field计数
    size_t fields() const
    {
        return _fields;
    }
    //! row计数
    void rows_count(int adj=1)
    {
        _rows += adj;
    }
    //! 返回總行数
    size_t rows() const
    {
        return _rows;
    }
    //! 设定列数
    void set_columns(size_t c)
    {
        _columns = c;
    }
    //! 返回列数
    size_t columns() const
    {
        return _columns;
    }
    //! 设置表头标志
    void settitle(bool t)
    {
        _btitle = t;
    }
    //! 是否已经分析完表头
    bool havetitle() const
    {
        return _btitle;
    }
    //! 输出csv表头信息和内容
    friend std::ostream& operator <<(std::ostream&, csvtable&);

    std::map< std::string, int > titlemap; ///< 表头index映射
    std::vector< std::string* > keylist; ///< 表索引链,用vector省点事吧
    size_t cur_col; ///< cb1函数保存数据索引用

    //! 显示文件名
    std::string name() const
    {
        return _filename;
    }
private:
    std::string _filename; // 文件名
    int table_type; // csv文件表类型 0:普通表,有唯一索引id值 1:索引id值不唯一
    size_t _fields; // 数据总个数
    size_t _rows; // 总行数
    size_t _columns; // 列数 columns = fields / rows
    bool _btitle; // 标志表头读了没有啊

    void (*fcb1)(void*, size_t, void*); // cb1函数指针
    void (*fcb2)(int, void*); // cb2函数指针
    struct csv_parser _parser;
    unsigned char _options; // csv分析选项
    //  关键字索引map
    /*
    key为表索引的id值, value为数组下标,这个下标对于keylist的下标
    */
    HashMap keyindexmap;
    HashMultiMap keyindexmultimap; // 表索引id值不唯一,用这个建立索引map
    int createindex(const char* zSql); // 建立索引id的map
#ifdef USE_SQLITE3
    sqlite3* db; // 库对象
#endif
};

#endif

--------------------------------------------------------------------------------------------

 

/*! \file csvtable.cpp
    fileencoding:utf-8
    \brief 读取csv文件函数实现
    \author 
    \date
*/
#include <fstream>
#include <sstream>
#include <iostream>
#include <cstring>
#include "csvtable.h"

void cb1(void* s, size_t len, void* data)
{
    csvtable* ct = (csvtable*)data;
    ct->fields_count();
    std::string str((char*)s, len);
    // std::cout<<"cb1:"<<len;
    if (!ct->havetitle())
    {
        // 处理titlemap
        // std::cout<<" cb11:"<<str<<"\n";
        ct->titlemap.insert(std::make_pair(str,ct->cur_col++));
    }
    else
    {
        // 首先检查游标
        if (ct->cur_col == ct->columns())
        {
            ct->cur_col = 0;
        }

        // std::cout<<" cur_col="<<ct->cur_col<<" cb12:"<<str<<'\n';

        // 赋值
        *((ct->keylist[ct->keylist.size()-1])+ct->cur_col) = str;

        // std::cout<<*((ct->keylist[ct->keylist.size()-1])+ct->cur_col)<<std::endl ;

        ct->cur_col++; // 最后移动游标
    }
}

void cb2(int c, void* data)
{
    csvtable* ct = (csvtable*)data;
    ct->rows_count();
    // std::cout<<"cb2\n";
    if (!ct->havetitle())
    {
        // csv表结构第一行是cloumn name 所以去掉這一行的計數
        ct->rows_count(-1);
        // 第一次执行cb2,此时的field计数就是列数
        ct->settitle(true);
        ct->set_columns(ct->fields());
    }
    ct->keylist.push_back(new std::string[ct->columns()]);
    // std::cout<<"new"<<ct->keylist.size()<<std::endl;
}

#ifndef USE_SQLITE3
static int is_space(unsigned char c)
{
    if (c == CSV_SPACE || c == CSV_TAB) return 1;
    return 0;
}

static int is_term(unsigned char c)
{
    if (c == CSV_CR || c == CSV_LF) return 1;
    return 0;
}
#endif
csvtable::csvtable()
{
    _fields = 0;
    _rows = 0;
    fcb1 = 0;
    fcb2 = 0;
    _options = 0;
    _btitle = false;
    cur_col = 0;
#ifdef USE_SQLITE3
    db = 0;
#endif
}

// 释放资源
csvtable::~csvtable()
{
    // 释放keylist new出来的string数组
    std::vector< std::string* >::iterator it;
    for (it=keylist.begin(); it!=keylist.end(); ++it)
    {
        // std::string* p = *it;
        delete [](*it);
    }
}
#ifndef USE_SQLITE3
int csvtable::Init(const char* filename,
                   int  table_type,
                   void (*fcb1)(void *, size_t, void *),
                   void (*fcb2)(int, void *))
{
    if (filename == 0) return -1;
    this->table_type = table_type;
    this->fcb1 = fcb1;
    this->fcb2 = fcb2;
    _filename = filename;
    // 检测文件是否存在
    std::ifstream infile(filename);
    if (!infile)
    {
        return -2;
    }
    if (csv_init(&_parser, _options) != 0)
    {
        return -3;
    }
    // 添加空白和结束符的定义
    csv_set_space_func(&_parser, is_space);
    csv_set_term_func(&_parser, is_term);
    return 0;
}


int csvtable:: read(const char* zSql)
{
    // 不可以重复读
    if (havetitle())
        return -6;
    FILE *fp;
    fp = fopen(_filename.c_str(), "rb");
    if (fp)
    {
        const int readsize = 8192;
        char line[readsize];
        size_t len;
        // magic word check
        if (!fgets(line, readsize, fp))
        {
            return -1;
        }
        if (strncmp(line, "\"FCTB1.00",strlen("\"FCTB1.00")) != 0
                && strncmp(line, "\"\"\"FCTB1.00",
                           strlen("\"\"\"FCTB1.00")) !=0
                && strncmp(line, "FCTB1.00",
                           strlen("FCTB1.00")) !=0 )
        {
            std::cout<<"magic word check failed."<<std::endl;
            return -2;
        }

        // ignore Chinese comment
        if (!fgets(line, readsize, fp))
        {
            return -6;
        }


        /* 重点在这里*/
        while ((len = fread(line, 1, readsize, fp)) > 0)
        {
            if (csv_parse(&_parser, line, len, fcb1, fcb2, this)!= len)
            {
                // error occur,立马挂掉
                fprintf(stderr, "Error while parsing file: %s\n",
                        csv_strerror(csv_error(&_parser)));
                exit(EXIT_FAILURE);
            }
        }

        csv_fini(&_parser, fcb1, fcb2, this);
        csv_free(&_parser);
        fclose(fp);
        // 列数检查
        if (columns() != _fields / (_rows+1))
        {
            std::cout<<"Error! columns="<<columns()
                     <<" field="<<_fields<<" rows="<<_rows;
            return  -3;
        }
        // 删除最后一个分配的未使用存储
        size_t size = keylist.size();
        if (size>0)
        {
            std::string* p = keylist[size-1];
            delete []p;
            keylist.pop_back();
        }

        // 建立索引map
        if (createindex(zSql)<0)
        {
            std::cout<<"createindex failed, return -4"<<std::endl;
            return -4;
        }
        return 0;
    }
    return -5;
}
#else // USE_SQLITE3
int csvtable::Init(const char* filename,
                   int  table_type,
                   void (*fcb1)(void *, size_t, void *),
                   void (*fcb2)(int, void *))
{
    if (filename == 0) return -1;
    this->table_type = table_type;
    _filename = filename;
    // 打开库文件
    int rc = sqlite3_open_v2(filename, &db, SQLITE_OPEN_READONLY, 0);
    if (rc)
    {
        std::cout<<sqlite3_errmsg(db)<<":"<<filename<<std::endl;
        sqlite3_close(db);
        return -2;
    }
    // 服务器就不要用加密了吧
    // // sqlite3_key(sdb, passwd, strlen(passwd));

    return 0;
}
int csvtable:: read(const char* zSql)
{
    if (zSql ==0 || db ==0)
    {
        return -1;
    }
    // 不可以重复读
    if (havetitle())
        return -6;
    // std::string zSql("select * from ");
    // zSql.append(view_name);
    // zSql.append(";");
    // 准备
    sqlite3_stmt* ppStmt; // Statement hanlde
    int rc = sqlite3_prepare_v2(db, zSql, -1, &ppStmt, 0);
    if (rc)
    {
        std::cout<<sqlite3_errmsg(db)<<std::endl;
        std::cout<<zSql<<std::endl;
        sqlite3_close(db);
        return -3;
    }
    rc = 0;
    while (1)
    {
        rc = sqlite3_step(ppStmt);
        if (rc == SQLITE_DONE)
        {
            // csv表结构第一行是cloumn name
            // sqlite查询没有这一行
            // 为了和原来的写的代码兼容,这里多加一行
            // rows_count();
            break;
        }
        else if (rc != SQLITE_ROW)
        {
            std::cout<<"step return:"<<rc<<std::endl;
            std::cout<<sqlite3_errmsg(db)<<std::endl;
            sqlite3_close(db);
            return -4;
        }
        rows_count(); // 行计数

        int count = sqlite3_column_count(ppStmt);
        if (!havetitle())
        {
            for (int i=0; i<count; i++)
            {
                std::string str = sqlite3_column_name(ppStmt, i);
                titlemap.insert(std::make_pair(str,i));
            }
            settitle(true);
            set_columns(count);
        }
        // 分配内存空间
        keylist.push_back(new std::string[columns()]);
        for (int i=0; i<count; i++)
        {
            fields_count(); // 计数
            (*((keylist[keylist.size()-1])+i)).assign((char*)sqlite3_column_blob(ppStmt, i), sqlite3_column_bytes(ppStmt, i));
        }
    }

    sqlite3_finalize(ppStmt); // 销毁处理语句对象
    // 关闭
    sqlite3_close(db);
    // 行数检查
    if (_rows == 0) // 没有数据
    {
        std::cout<<"Warning! "<<zSql<<" row="<<_rows<<" !\n";
        return 0;
    }
    // 列数检查
    if (columns() != _fields / _rows)
    {
        std::cout<<"Error! columns="<<columns()
                 <<" field="<<_fields<<" rows="<<_rows;
        return  -3;
    }

    // 建立索引map
    if (createindex(zSql)<0)
    {
        std::cout<<"createindex failed, return -4"<<std::endl;
        return -4;
    }
    return 0;
}
#endif
int csvtable::createindex(const char* zSql)
{
    std::cout<<name()<<" "<<zSql<<"|createindex:"<<table_type;
    switch (table_type)
    {
        // 唯一索引表,用hashmap
    case 0:
    {
        for (size_t i=0; i<keylist.size(); i++)
        {
            keyindexmap.insert(std::make_pair(atoi((*keylist[i]).c_str()), i));
        }
        break;
    }
    // 不唯一索引,用hashmultimap
    case 1:
    {
        for (size_t i=0; i<keylist.size(); i++)
        {
            keyindexmultimap.insert(std::make_pair(atoi((*keylist[i]).c_str()), i));
        }
        break;
    }
    default:
    {
        exit(EXIT_FAILURE);
    }
    }
    std::cout<<" done\n ";
    return 0;
}

std::ostream& operator <<(std::ostream& os, csvtable& ct)
{
    os<<ct.name()<<"\ntotal columns="<<ct.columns()<<" rows="<<ct.rows()<<" field="<<ct.fields();
    // 输出title
    if (!ct.titlemap.empty())
    {
        os<<'\n';
        std::map<std::string, int>::iterator it;
        int i=0;
        for (it=ct.titlemap.begin(); it!=ct.titlemap.end(); it++,i++)
        {
            os<<i<<':'<<it->first<<' ';
        }

        switch (ct.table_type)
        {
        case 0:
        {   // index
            HashMap::iterator pos;
            os<<"\nkey:row index\n";
            for (pos=ct.keyindexmap.begin(); pos!=ct.keyindexmap.end(); pos++)
            {
                os<<pos->first<<':'<<pos->second<<'\n';
            }
            // content
            for (size_t key=0; key< ct.keylist.size(); key++)
            {
                os<<"row="<<key;
                for (size_t j=0; j< ct.columns(); j++)
                {
                    os<<" col="<<j<<" v="<<*(ct.keylist[key]+j);
                    if (j != ct.columns()-1)
                        os<<' ';
                }
                if (key != ct.keylist.size()-1)
                    os<<'\n';
            }
            break;
        }
        case 1:
        {   // index
            HashMultiMap::iterator pos;
            os<<"\nmulti key:row index\n";
            for (pos=ct.keyindexmultimap.begin(); pos!=ct.keyindexmultimap.end(); pos++)
            {
                os<<pos->first<<':'<<pos->second<<'\n';
            }
            // content
            for (size_t key=0; key< ct.keylist.size(); key++)
            {
                os<<"row="<<key;
                for (size_t j=0; j< ct.columns(); j++)
                {
                    os<<" col="<<j<<" v="<<*(ct.keylist[key]+j);
                    if (j != ct.columns()-1)
                        os<<' ';
                }
                if (key != ct.keylist.size()-1)
                    os<<'\n';
            }
            break;
        }
        default:
            break;
        }
    }
    return os;
}

 


对象实体和初始化


//! 技能升级加点消耗表
csvtable TSkillUpConsume;

struct STData
{
    class csvtable& table; ///< 實例
    const char* filename; ///< 數據所在文件名稱
    int table_type; ///< 數據表類型0或1
    const char* zsql; ///< 生成數據表對應的sql查詢語句
};

STData DataTables[] =
{
    {TSkillUpConsume,DATAFILE,0, "select * from b_repair;"},
     .... // 这里添加各个表信息

};

 

 

// 初始化各种表对象

int InitDataTables(std::string path)
{
    if (path[path.size()-1]!='/')
        path.push_back('/');
    for (size_t i=0; i<sizeof(DataTables)/sizeof(STData); i++)
    {
        std::string fullname = path+DataTables[i].filename;
        int ret = DataTables[i].table.Init(fullname.c_str(), DataTables[i].table_type);
        if ( ret != 0)
        {
            // 初始化有问题,直接挂掉
            errf<<ret<<" Init,InitDataTable "<<fullname<<" "<<DataTables[i].zsql;
            exit(EXIT_FAILURE);
        }
        ret =DataTables[i].table.read(DataTables[i].zsql);
        if ( ret != 0)
        {
            // 读表问题,直接挂掉
            errf<<ret<<" read,InitDataTable "<<fullname<<" "<<DataTables[i].zsql;
            exit(EXIT_FAILURE);
        }
        debugf<<"Read "<<fullname<<" "<<DataTables[i].zsql<<" OK.";
    }

    return 0;
}


0 0
原创粉丝点击