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

最近写了个读取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;

      return ostr.str();
}

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

      return returnVal;
}

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_t const &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_t const &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_t sepPos = 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::string tmp_value;
      get_value_of_key_section(f, start, section, key, tmp_value, def_value);

      return Convert::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)
  {
      return find_next_section(f, name, std::ios_base::beg);
  }
//返回配置文件中特定section名字的个数
  int count_section(std::ifstream &f, const std::string &name);
//返回一个section中特定key的个数
  int count_key(std::ifstream &f, size_t start, 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 be found!\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()
  {
      return conf;
  }

  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_t sepPos = temp.find(']');
    std::string tmp = temp.substr(sepPos + 1);
    return onlyWhitespace(tmp);
 
  }
  else
    return false;
}
 
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::string temp = 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_t start,
                                 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, const std::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] == '=')
    return false;
 
  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::string temp = 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_t sepPos = temp.find('=');
 
    std::string key_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_t sepPos = line.find('=');
    extractValue(value, sepPos, line);
   
    return re;
  }
 
  value = def_value;
  return start;
}
 
ConfigFile::~ConfigFile()
{
  if (file)
    file.close();
 
  BACKEND_SCHEMA *schema = conf->schemas;
  while (schema)
  {
    delete schema->rwserver;
    BACKEND_SERVER *bserver = schema->rdservers;
    while (bserver)
    {
      BACKEND_SERVER *tmp = bserver;
      bserver = bserver->next;
      delete tmp;
    }
    BACKEND_SCHEMA *tmp = schema;
    schema = schema->next;
    delete tmp;
  }
  SERVER_INFO *server = conf->servers;
  while (server)
  {
    SERVER_INFO *tmp = server;
    server = server->next;
    delete tmp;
  }
  delete conf;
}
 
 
void print_conf(PROXY_CONFIG *conf)
{
  std::cout<<"...start to print PROXY_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 print PROXY_CONF."<<'\n';
}
 
 
int main()
{
  ConfigFile cfg("proxy.cnf");
  print_conf(cfg.get_proxy_conf());
 
 
    std::cin.get();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
纯c读ini配置文件 用c/c++ini配置文件有不少第三方的开源库,如iniparser、libini、rwini、UltraLightINIParser等,但都不理想,往往代码较大、功能较弱、 接口使用不方便。尤其在大小处理、前后空格、各种注释、跨平台换行符支持、带引号字符串处理、无section操作、原格式保持等方面存在问题。 现将本人精心制作的ini程序源码奉献给大家,纯c编,简洁好用。支持windows和linux。 主要特点: 1、支持;和#注释符号,支持行尾注释。 2、支持带引号'或"成对匹配的字符串,提取时自动去引号。引号中可带其它引号或;#注释符。 3、支持无section或空section(称为空)。 4、支持10、16、8进制数,0x开头为16进制数,0开头为8进制。 5、支持sectionkey或=号前后带空格。 6、支持\n、\r、\r\n或\n\r换行格式。 7、不区分sectionkey大小,但写入时以新串为准,并保持其大小。 8、新增数据时,若section存在则在该节最后一个有效数据后添加,否则在文件尾部添加。 9、支持指定key所在整行删除,即删除该键值,包括注释。 10、可自动跳过格式错误行,修改时仍然保留。 11、修改时保留原注释:包括整行注释、行尾注释(包括前面空格)。 12、修改时保留原空行。以上三点主要是尽量保留原格式。 不足之处: 1、不支持key多value(逗号分割),只能一次性提取后自行处理。 2、不支持同名重复sectionkey。(重复section可视为错误,重复key则可能造成分歧) 3、不能提取所有sectionkey称。 使用只需两个文件inirw.h、inirw.c,另有测试程序和工程文件支持windows和linux。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值