最近写了个读取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 §ion)
{
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 §ion,
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 §ion,
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 §ion,
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 §ion,
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
读取的配置文件如下:
# 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
real_time_queries = 2
[schema]
dbname = db1
user =
password = 123
rwserver = "master 10 100
rdserver = "master2 10 80
rdserver = "slave 10 80
[schema]
dbname = db2
user =
password = 123
rwserver = "master 10 100
rdserver = "master2 10 80
rdserver = "slave 10 80
可以看出配置文件中有多个同名的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
{
} SERVER_INFO;
typedef struct BACKEND_SERVER
{
} BACKEND_SERVER;
typedef struct BACKEND_SCHEMA
{
} BACKEND_SCHEMA;
//这个结构体是配置文件的总结构体,它包含了所有的配置。
typedef struct PROXY_CONFIG
{
} PROXY_CONFIG;
void print_conf(PROXY_CONFIG *conf);
//为了取值方便,定义了Convert 工具类,主要是通过模板来简化代码。
class Convert
{
public:
};
template
std::string Convert::T_to_string(T const &val)
{
}
template
T Convert::string_to_T(std::string const &val)
{
}
template <>
std::string Convert::string_to_T (std::string const &val)
{
}
// 主要的实现逻辑定义在ConfigFile类中
class ConfigFile
{
private:
//该方法用来将注释行去掉
//判断这行是否为空行,即除了空格什么也没有
//这个函数用来验证 key=value 模式,只有符合这个模式才是合法的
//从行字符串中提取key
//从行字符串中提取value
//判断这一行是不是一个section,即是否符合 "[ name]" 这个模式
//获取section的名字
//获取从给定位置开始的section中key的名为给定值的行
//获取从给定位置开始的section中给定key的value。 它是get_key_of_section 和 extractValue的封装
//获取给定类型value值
template
//获取下一个特定名字的section在文件中的位置。
//返回配置文件中特定section名字的个数
//返回一个section中特定key的个数
public:
//以下几个方法是示例,每个方法分别读取一种section
};
#endif //end CONFIG_FILE_H
然后是实现文件main.cc, 对于多重名的section和key的处理思路是先统计文件中有个给定名的section/key,然后对他们分别读取,每次读取之后都更新文件的读取位置,避免读重复了。 具体例子可以参考read_schemas():
#include
#include
#include
#include