写个支持多重名section和key的读取…

来源:互联网 发布:canonmp288清零软件 编辑:程序博客网 时间:2024/06/06 06:55
最近写了个读取ini配置文件的c++工具,它支持多重名section和key的读取

读取的配置文件如下:
# This is a comment
[common]
name = proxy
port = 5588

[default_server]
check_user = rpl
check_password = 123
master_backup = on

[server]
alisa = slave
ip = 172.16.72.11
port = 3307

[server]
alias = master
ip = 172.16.72.10
port = 3306

[server]
alias = master2
ip = 172.16.72.10
port = 3306

[default_schema]
rw_splitting    = 1
real_time_queries = 2

[schema]
dbname = db1
user =  user_1
password = 123
rwserver = "master 10 100  5 2"
rdserver = "master2 10 80  6 2"
rdserver = "slave 10 80  6 2"

[schema]
dbname = db2
user =  user_2
password = 123
rwserver = "master 10 100  5 2"
rdserver = "master2 10 80  6 2"
rdserver = "slave 10 80  6 2"

可以看出配置文件中有多个同名的section (如schema, server)和多个同名的key(如rdserver)。

然后看看代码:
首先是头文件 ConfigFile.h

#ifndef CONFIG_FILE_H
#define CONFIG_FILE_H

void exitWithError(const std::string &error);

//为了读取配置文件方便,根据业务需求定义些结构体。

// several user define structs begin
typedef struct SERVER_INFO
{
  std::string alias;
  std::string ip;
  int port;
  SERVER_INFO *next;
} SERVER_INFO;

typedef struct BACKEND_SERVER
{
  std::string server;
  BACKEND_SERVER *next;
} BACKEND_SERVER;

typedef struct BACKEND_SCHEMA
{
  std::string db_name;
  std::string user;
  std::string password;

  BACKEND_SERVER *rwserver;
  BACKEND_SERVER *rdservers;

  BACKEND_SCHEMA *next;

} BACKEND_SCHEMA;


//这个结构体是配置文件的总结构体,它包含了所有的配置。

typedef struct PROXY_CONFIG
{
  //common
  std::string name;
  int port;

  //def_server
  std::string check_user;
  std::string check_password;
  bool master_backup;

  //def_schema
  int rw_splitting;
  int real_time_queries;

  SERVER_INFO *servers;

  BACKEND_SCHEMA *schemas;

} PROXY_CONFIG;

void print_conf(PROXY_CONFIG *conf);

//为了取值方便,定义了Convert 工具类,主要是通过模板来简化代码。

class Convert
{
public:
  template
  static std::string T_to_string(T const&val);

  template
  static T string_to_T(std::string const&val);

};

template
std::string Convert::T_to_string(T const &val)
{
   std::ostringstream ostr;
    ostr<< val;

    returnostr.str();
}

template
T Convert::string_to_T(std::string const &val)
{
   std::istringstream istr(val);
    TreturnVal;
    if (!(istr>> returnVal))
     exitWithError("CFG: Not a valid " + (std::string)typeid(T).name() +" received!\n");

    returnreturnVal;
}

template <>
std::string Convert::string_to_T (std::string const &val)
{
  return val;
}

// 主要的实现逻辑定义在ConfigFile类中

class ConfigFile
{
private:
  std::string fName;
  std::ifstream file;
  PROXY_CONFIG *conf;

//该方法用来将注释行去掉
  void removeComment(std::string &line)const
  {
    if(line.find('#') != line.npos)
     line.erase(line.find('#'));
  }
//判断这行是否为空行,即除了空格什么也没有
  bool onlyWhitespace(const std::string &line)const
  {
    return(line.find_first_not_of(' ') == line.npos);
  }
//这个函数用来验证 key=value 模式,只有符合这个模式才是合法的
  bool validLine(const std::string &line)const;

//从行字符串中提取key
  void extractKey(std::string &key, size_tconst &sepPos, const std::string &line) const
  {
    key =line.substr(0, sepPos);
    if(key.find('\t') != line.npos || key.find(' ') != line.npos)
     key.erase(key.find_first_of("\t "));
  }

//从行字符串中提取value
  void extractValue(std::string &value, size_tconst &sepPos, const std::string &line) const
  {
    value =line.substr(sepPos + 1);
   value.erase(0, value.find_first_not_of("\t "));
   value.erase(value.find_last_not_of("\t ") + 1);
  }

//判断这一行是不是一个section,即是否符合 "[ name]" 这个模式
  bool is_section(const std::string &line)const;

//获取section的名字
  void extraSectionName(const std::string&line, std::string &section)
  {
    size_tsepPos = line.find('[');
    section =line.substr(sepPos + 1, line.find(']') - 1);
  }

//获取从给定位置开始的section中key的名为给定值的行
  size_t get_key_of_section(std::ifstream &f,int start, const std::string &section,
                           const std::string &key, std::string &line);

//获取从给定位置开始的section中给定key的value。 它是get_key_of_section 和extractValue的封装
  size_t get_value_of_key_section(std::ifstream&f, int start, const std::string &section,
                                 const std::string &key, std::string &value,
                                 const std::string &def_value);

//获取给定类型value值 
template
  ValueType get_type_value_of_key(std::ifstream&f, int start, const std::string &section,
                                 const std::string &key,const std::string &def_value)
  {
    std::stringtmp_value;
   get_value_of_key_section(f, start, section, key, tmp_value,def_value);

    returnConvert::string_to_T(tmp_value);
  }
//获取下一个特定名字的section在文件中的位置。
  size_t find_next_section(std::ifstream &f,const std::string &name, size_t start);

  size_t find_first_section(std::ifstream &f,const std::string &name)
  {
    returnfind_next_section(f, name, std::ios_base::beg);
  }
//返回配置文件中特定section名字的个数
  int count_section(std::ifstream &f, conststd::string &name);
//返回一个section中特定key的个数
  int count_key(std::ifstream &f, size_tstart, const std::string &section,
               const std::string &key);
public:
  ConfigFile(const std::string &fName)
  {
   this->fName = fName;
   this->conf = new PROXY_CONFIG;;
   file.open(this->fName.c_str());
    if(!file)
     exitWithError("CFG: File " + fName + " couldn't befound!\n");
   read_test();
   //ExtractKeys();
  }
  ~ConfigFile();
//以下几个方法是示例,每个方法分别读取一种section
  void read_common();
  void read_def_server();
  void read_servers();
  void read_def_schema();
  void read_schemas();

  PROXY_CONFIG *get_proxy_conf()
  {
    returnconf;
  }

  void read_test()
  {
   read_common();
   read_def_server();
   read_servers();
   read_def_schema();
   read_schemas();
  }
};

#endif //end CONFIG_FILE_H

然后是实现文件main.cc,对于多重名的section和key的处理思路是先统计文件中有个给定名的section/key,然后对他们分别读取,每次读取之后都更新文件的读取位置,避免读重复了。具体例子可以参考read_schemas():

#include
#include
#include
#include
#include
#include
#include
#include "ConfigFile.h"
 
void exitWithError(const std::string &error)
{
    std::cout<< error;
   std::cin.ignore();
   std::cin.get();
 
   exit(EXIT_FAILURE);
}
 
void ConfigFile::read_schemas()
{
  int i, count, j, cnt;
  std::string tmp_value;
  size_t pos = std::ios_base::beg;
 
  count = count_section(file, "schema");
  conf->schemas = NULL;
  for (i = 0; i < count; i++)
  {
   BACKEND_SCHEMA *schema = new BACKEND_SCHEMA;
    pos =find_next_section(file, "schema", pos);
   get_value_of_key_section(file, pos, "schema", "db_name", tmp_value,"def_db");
   schema->db_name = tmp_value;
   get_value_of_key_section(file, pos, "schema", "user", tmp_value,"def_user");
   schema->user = tmp_value;
   get_value_of_key_section(file, pos, "schema", "password",tmp_value, "def_password");
   schema->password = tmp_value;
   get_value_of_key_section(file, pos, "schema", "rwserver",tmp_value, "null");
   BACKEND_SERVER *rwserver = new BACKEND_SERVER;
   rwserver->server = tmp_value;
   schema->rwserver = rwserver;
    cnt =count_key(file, pos, "schema", "rdserver");
   schema->rdservers = NULL;
    for (j = 0;j < cnt; j++)
    {
     BACKEND_SERVER *rdserver = new BACKEND_SERVER;
     pos = get_value_of_key_section(file, pos, "schema", "rdserver",tmp_value, "null");
     rdserver->server = tmp_value;
     rdserver->next = schema->rdservers;
     schema->rdservers = rdserver;
    }
   schema->next = conf->schemas;
   conf->schemas = schema;
  }
}
 
void ConfigFile::read_def_schema()
{
  size_t pos;
  std::string tmp_value;
  pos = find_first_section(file,"default_schema");
  int rwsplitting = get_type_value_of_key (file,pos, "default_schema",
                                               "rw_splitting", "1");
  conf->rw_splitting = rwsplitting;
  int real_time_queries = get_type_value_of_key(file, pos, "default_schema",
                                               "real_time_queries", "1");
  conf->real_time_queries =real_time_queries;
}
 
void ConfigFile::read_def_server()
{
  size_t pos;
  std::string tmp_value;
  pos = find_first_section(file,"default_server");
  get_value_of_key_section(file, pos, "common","check_user", tmp_value, "def_rpl");
  conf->check_user = tmp_value;
  get_value_of_key_section(file, pos, "common","check_password", tmp_value, "def_123");
  conf->check_user = tmp_value;
  get_value_of_key_section(file, pos, "common","master_backup", tmp_value, "def_on");
  conf->master_backup = (tmp_value == "on") ? 1: 0;
}
 
void ConfigFile::read_common()
{
  size_t pos;
  std::string tmp_value;
  pos = find_first_section(file, "common");
  get_value_of_key_section(file, pos, "common","name", tmp_value, "great_proxy");
  conf->name = tmp_value;
//get_value_of_key_section(file, pos, "common", "port", tmp_value,"5588");
  int port = get_type_value_of_key (file, pos,"common", "port", "5588");
  conf->port = port;
}
 
void ConfigFile::read_servers()
{
  int i, count;
  std::string tmp_value;
  size_t pos = std::ios_base::beg;
  count = count_section(file, "server");
  conf->servers = NULL;
  for (i = 0; i < count; i++)
  {
    pos =find_next_section(file, "server", pos);
    SERVER_INFO* server = new SERVER_INFO;
   get_value_of_key_section(file, pos, "server", "alias", tmp_value,"def_name");
   server->alias = tmp_value;
   get_value_of_key_section(file, pos, "server", "ip", tmp_value,"0.0.0.0");
   server->alias = tmp_value;
    int port =get_type_value_of_key (file, pos, "server", "port", "3306");
   server->port = port;
   server->next = conf->servers;
   conf->servers = server;
  }
}
 
inline bool ConfigFile::is_section(const std::string &line)const
{
  std::string temp = line;
  temp.erase(0, temp.find_first_not_of("\t"));
  if (temp[0] == '[')
  {
    size_tsepPos = temp.find(']');
    std::stringtmp = temp.substr(sepPos + 1);
    returnonlyWhitespace(tmp);
 
  }
  else
    returnfalse;
}
 
inline size_t ConfigFile::find_next_section(std::ifstream&f,
                                           const std::string &name, size_t start)
{
  std::string line;
 
  f.clear();
 
  f.seekg(start, std::ios_base::beg);
 
  while (std::getline(f, line))
  {
    std::stringtemp = line;
    if(temp.empty())
     continue;
 
   removeComment(temp);
    if(onlyWhitespace(temp))
     continue;
    if(is_section(temp))
    {
     std::string sn;
     extraSectionName(temp, sn);
     if (sn == name)
       return f.tellg();
    }
  }
  return 0;
}
 
inline int ConfigFile::count_key(std::ifstream &f, size_tstart,
                                const std::string &section,
                                const std::string &key)
{
  int i = 0;
  size_t re = start;
  std::string line;
 
  do
  {
    start =re;
    re =get_key_of_section(f, start, section, key, line);
    if (re >start)
     i++;
  }while(re > start);
 
  return i;
}
 
inline int ConfigFile::count_section(std::ifstream &f, conststd::string &name)
{
  int i = 0;
  size_t pos = std::ios_base::beg;
 
  do
  {
    pos =find_next_section(f, name, pos);
    if (pos >0)
    {
     i++;
    }
  }while(pos > 0);
 
  return i;
}
 
 
inline bool ConfigFile::validLine(const std::string &line)const
{
  std::string temp = line;
  temp.erase(0, temp.find_first_not_of("\t"));
  if (temp[0] == '=')
    returnfalse;
 
  for (size_t i = temp.find('=') + 1; i <temp.length(); i++)
    if (temp[i]!= ' ')
     return true;
 
  return false;
}
 
inline size_t ConfigFile::get_key_of_section(std::ifstream &f,int start,
                                            const std::string &section,
                                            const std::string &key,
                                            std::string &line)
{
  f.clear();
  f.seekg(start, std::ios_base::beg);
 
  while (std::getline(f, line))
  {
    std::stringtemp = line;
    if(temp.empty())
     continue;
 
   removeComment(temp);
    if(onlyWhitespace(temp))
     continue;
 
    if(is_section(temp))
     break;
 
   temp.erase(0, temp.find_first_not_of("\t "));
    size_tsepPos = temp.find('=');
 
    std::stringkey_tmp;
   extractKey(key_tmp, sepPos, temp);
    if (key_tmp== key)
    {
     line = temp;
     return f.tellg();
    }
   continue;
  }
 
  return start;
}
 
inline size_t ConfigFile::get_value_of_key_section(std::ifstream&f, int start,
                                                  const std::string &section,
                                                  const std::string &key, std::string &value,
                                                  const std::string &def_value)
{
  std::string line;
 
  size_t re = get_key_of_section(f, start,section, key, line);
  if (re > start)
  {
    size_tsepPos = line.find('=');
   extractValue(value, sepPos, line);
   
    returnre;
  }
 
  value = def_value;
  return start;
}
 
ConfigFile::~ConfigFile()
{
  if (file)
   file.close();
 
  BACKEND_SCHEMA *schema = conf->schemas;
  while (schema)
  {
    deleteschema->rwserver;
   BACKEND_SERVER *bserver = schema->rdservers;
    while(bserver)
    {
     BACKEND_SERVER *tmp = bserver;
     bserver = bserver->next;
     delete tmp;
    }
   BACKEND_SCHEMA *tmp = schema;
    schema =schema->next;
    deletetmp;
  }
  SERVER_INFO *server = conf->servers;
  while (server)
  {
    SERVER_INFO*tmp = server;
    server =server->next;
    deletetmp;
  }
  delete conf;
}
 
 
void print_conf(PROXY_CONFIG *conf)
{
  std::cout<<"...start to printPROXY_CONF..."<<'\n';
  std::cout<<"section:common:"<<'\n';
 std::cout<<"   name: "<< conf->name <<'\n';
 std::cout<<"   port: "<< conf->port<<'\n';
  std::cout<<"section:default_server:"<<'\n';
 std::cout<<"   check_user: "<< conf->check_user<<'\n';
 std::cout<<"   check_password: "<<conf->check_password<<'\n';
 std::cout<<"   master_backup: "<< conf->master_backup<<'\n';
  std::cout<<"  servers:"<<'\n';
  SERVER_INFO *server = conf->servers;
  while (server)
  {
   std::cout<<"       alias: "<< server->alias<<'\n';
   std::cout<<"       ip: "<< server->ip<<'\n';
   std::cout<<"       port: "<< server->port<<'\n';
   std::cout<<'\n';
    server =server->next;
  }
  std::cout<<"section:default_schema:"<<'\n';
 std::cout<<"   rw_splitting: "<< conf->rw_splitting<<'\n';
 std::cout<<"   real_time_queries: "<<conf->real_time_queries<<'\n';
  std::cout<<"  schemas:"<<'\n';
  BACKEND_SCHEMA *schema = conf->schemas;
  while (schema)
  {
   std::cout<<"       db_name: "<< schema->db_name<<'\n';
   std::cout<<"       user: "<< schema->user<<'\n';
   std::cout<<"       password: "<< schema->password<<'\n';
   std::cout<<"       rwserver: "<<schema->rwserver->server<<'\n';
   BACKEND_SERVER *bserver = schema->rdservers;
    while(bserver)
    {
     std::cout<<"       rdserver: "<< bserver->server<<'\n';
     bserver = bserver->next;
    }
    schema =schema->next;
   std::cout<<'\n';
  }
 
  std::cout<<"end of printPROXY_CONF."<<'\n';
}
 
 
int main()
{
  ConfigFile cfg("proxy.cnf");
  print_conf(cfg.get_proxy_conf());
 
 
   std::cin.get();
    return0;
}
0 0