Skip to content

Latest commit

 

History

History
216 lines (189 loc) · 7.77 KB

10.1_ini配置.md

File metadata and controls

216 lines (189 loc) · 7.77 KB

10.1 ini配置

  我们先来看下ini的配置文件是啥,例如我们的配置文件如下,database就是我们的俗称的区段,ip,port就是字段,127.0.0.1和3306就是字段的值。

[database]
ip       = 127.0.0.1 ;
port     = 3306 ;
user     = root ;
pwd      = 123456 ;
db       = dongnaobike;

[server]
port     = 9090

  OK,那我们怎么实现从ini文件里加载上这些内容呢?首先分析就是读取文件内容,然后依照文件内容,把这些字段和值组成key-value的形式保存在某个数据结构中,这样我们通过key(区段+字段)就能找到value(值),那如何设计这个数据结构呢?
  最容易想到的就是字典了(dictionary),这个在我们的数据结构的课堂上应该学过吧,字典是一个非常常用的数据结构了,如果大家有兴趣还可以去深度探索下redis的存储结构,也用到了字典,只不过它的那个字典设计更加复杂而已。

typedef struct _dictionary_ {
    int            n ;  /** 字典中已经存储的个数 */
    ssize_t        size ;  /** 字典中节点的个数 */
    char        **  val ;  /** value列表 */
    char        **  key ;  /** key列表 */
    unsigned     *  hash ;  /** hash值列表 */
} dictionary ;

  那他是如何存储数据的呢?我们首先来看它还应当提供的接口:

dictionary * dictionary_new(size_t size);
void dictionary_del(dictionary * vd);
unsigned dictionary_hash(const char * key);
const char * dictionary_get(const dictionary * d, const char * key, const char * def);
int dictionary_set(dictionary * vd, const char * key, const char * val);
void dictionary_unset(dictionary * d, const char * key);
void dictionary_dump(const dictionary * d, FILE * out);

  自然不用说,我们首先得通过dictionary_new接口创建一个字典,创建这个接口的实现如下,其实val和key都是字符串的二维数组,hash是一个无符号整形的数组,创建一个字典就是首先分配内存:

dictionary * dictionary_new(size_t size)
{
    dictionary  *   d ;

    /* If no size was specified, allocate space for DICTMINSZ */
    if (size<DICTMINSZ) size=DICTMINSZ ;

    d = (dictionary*) calloc(1, sizeof *d) ;

    if (d) {
        d->size = size ;
        d->val  = (char**) calloc(size, sizeof *d->val);
        d->key  = (char**) calloc(size, sizeof *d->key);
        d->hash = (unsigned*) calloc(size, sizeof *d->hash);
    }
    return d ;
}

  创建了字典,那如何往字典里插入key-value对呢?上面的这个数据结构其实就是这样的一个东西,用图表示如下: 字典结构 从上图,我们不难理解,就是在插入key-value时,首先计算出一个key的hash值(hash),然后把hash-key-value三元组存放在上图的结构的位置d->n位置,最后d->n移动一个位置,只不过我们在存放这个hash-key-value这个三元组之前要做各种判断,如:

  • hash值是否在字典中存在,如果存在则更新key和value;
  • 是否已经超出了字典的大小,如果是的则需要扩充字典的节点个数(当前大小的两倍);
  • 找到d->n位置;
int dictionary_set(dictionary * d, const char * key, const char * val)
{
    ssize_t         i ;
    unsigned       hash ;

    if (d==NULL || key==NULL) return -1 ;

    /* Compute hash for this key */
    hash = dictionary_hash(key) ;
    /* Find if value is already in dictionary */
    if (d->n>0) {
        for (i=0 ; i<d->size ; i++) {
            if (d->key[i]==NULL)
                continue ;
            if (hash==d->hash[i]) { /* Same hash value */
                if (!strcmp(key, d->key[i])) {   /* Same key */
                    /* Found a value: modify and return */
                    if (d->val[i]!=NULL)
                        free(d->val[i]);
                    d->val[i] = (val ? xstrdup(val) : NULL);
                    /* Value has been modified: return */
                    return 0 ;
                }
            }
        }
    }
    /* Add a new value */
    /* See if dictionary needs to grow */
    if (d->n==d->size) {
        /* Reached maximum size: reallocate dictionary */
        if (dictionary_grow(d) != 0)
            return -1;
    }

    /* Insert key in the first empty slot. Start at d->n and wrap at
       d->size. Because d->n < d->size this will necessarily
       terminate. */
    for (i=d->n ; d->key[i] ; ) {
        if(++i == d->size) i = 0;
    }
    /* Copy key */
    d->key[i]  = xstrdup(key);
    d->val[i]  = (val ? xstrdup(val) : NULL) ;
    d->hash[i] = hash;
    d->n ++ ;
    return 0 ;
}

  那如何从字典中取数据呢?我们先来看接口定义:

const char * dictionary_get(const dictionary * d, const char * key, const char * def);

也就是要通过key返回对应的value,从上图我们更加要知道,我们去找这个value就是要找到对应的key的hash值所在的位置,也就是首先计算key的hash值,得到这个hash值后从d->hash数组里遍历出hash值,然后对应数组的下标 i 就是key和value所在d->key 和 d->value的位置,这样取走就行。好的,来看实现:

const char * dictionary_get(const dictionary * d, const char * key, const char * def)
{
    unsigned    hash ;
    ssize_t      i ;

    hash = dictionary_hash(key);
    for (i=0 ; i<d->size ; i++) {
        if (d->key[i]==NULL)
            continue ;
        /* Compare hash */
        if (hash==d->hash[i]) {
            /* Compare string, to avoid hash collisions */
            if (!strcmp(key, d->key[i])) {
                return d->val[i] ;
            }
        }
    }
    return def ;
}

OK,了解了最基本的数据结构,那就是解析ini文件内容了,这个不再细讲,详见开源库的实现https://github.com/ndevilla/iniparser

最终来看下我们如何使用这个开源库的:

typedef struct st_env_config
{
   //数据库的配置
   std::string db_ip;
   unsigned short db_port;
   std::string db_user;
   std::string db_pwd;
   std::string db_name;

   //服务的配置
   unsigned short svr_port;

   st_env_config()
   {
   };

   st_env_config(const std::string& db_ip, unsigned int db_port, const std::string& db_user, \
                 const std::string& db_pwd, const std::string& db_name, unsigned short svr_port)
   {
       this->db_ip    = db_ip;
       this->db_port  = db_port;
       this->db_user  = db_user;
       this->db_pwd   = db_pwd;
       this->db_name  = db_name;
       this->svr_port = svr_port;
   };

   st_env_config& operator =(const st_env_config& config)
   {
       if (this != &config)
       {
		   this->db_ip    = config.db_ip;
		   this->db_port  = config.db_port;
		   this->db_user  = config.db_user;
		   this->db_pwd	  = config.db_pwd;
		   this->db_name  = config.db_name;
		   this->svr_port = config.svr_port;

        }
        return *this;
    }
}_st_env_config;

bool Iniconfig::loadfile(const std::string& path)
{
    dictionary*   ini = NULL;

    ini = iniparser_load(path.c_str());
    if (ini==NULL)
    {
        LOG_ERROR("cannot parse file: %s\n", path.c_str());
        return false;
    }

    char* ip    = iniparser_getstring(ini, "database:ip", "127.0.0.1");
    int   port  = iniparser_getint(ini, "database:port", 3306);
    char* user  = iniparser_getstring(ini, "database:user", "root");
    char* pwd   = iniparser_getstring(ini, "database:pwd", "123456");
    char* db    = iniparser_getstring(ini, "database:db", "dongnaobike");
    int   sport = iniparser_getint(ini, "server:port", 9090);

    _config = st_env_config(std::string(ip), port, std::string(user), \
        std::string(pwd), std::string(db), sport);

    iniparser_freedict(ini);

    _isloaded = true;

    return true;
}