前言
对于很多程序中要用的参数如果是可变的,那么最好的处理方式就是通过main函数参数传递【就是我们经常见到的int main (int argc,char *argv[]){…}中的argc
以及argv
】,或者从别的地方去获取,这其中之一就是配置文件
,但是在一个成熟和架构完善的系统,一般都会做到自动配置,自动部署,所以有的系统里会有一个单独的配置服务存在,每个其它的服务的配置信息从配置中心服务获取,然后运维人员通过操作界面把配置信息下发给配置中心服务,其余服务从配置中心获取变更信息。
几乎每一个大型互联网项目都会涉及到项目配置,如mysql 的配置文件:
自己写的一个ini配置文件👇:
[database]
ip = 127.0.0.1 ;
port = 3306 ;
user = root ;
pwd = 123456 ;
db = mybike;
[server]
port = 9090;
我们先来看下ini的配置文件,例如我们的配置文件如下,[database]
就是我们的俗称的区段,ip
,port
就是字段,127.0.0.1和3306就是字段的值。
那么要如何对ini文件进行解析呢,首先想到的是不是使用文件流将文件中的内存读取出来,然后通过hash来进行key-value的存储,但是这样就会麻烦了,我们可以用别人写好的开源库iniparser来解析配置文件iniparser在github上的地址。
iniparser的安装
git clone https://github.com/ndevilla/iniparser
cd iniparser
make
make 以后会生成libiniparser.so.1
以及libiniparser.a
,而src目录下有dictionary.h
以及iniparser.h
在写工程级的代码时,一般会有test
、src
文件夹、conf
和third
文件夹,其中third
文件夹就是用来存放第三方头文件源文件以及库的
在这里插入图片描述
当我们make以后生成库以及头文件,将库、头文件以及cpp文件放入到自己项目的third
文件夹中
ini文件通过iniparser进行解析
先来展示下我的ini文件
shared_bike.ini
[database]
ip = 127.0.0.1 ;
port = 3306 ;
user = root ;
pwd = 123456 ;
db = mybike;
[server]
port = 9090;
代码展示
configdef.h
将后续通过iniparser解析出来的数据保存到st_env_config结构中
#ifndef SHBK_COMMON_CONFIGDEF_H
#define SHBK_COMMON_CONFIGDEF_H
#include <string>
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;
#endif//SHBK_COMMON_CONFIGDEF_H
iniconfig.h
将解析操作分装成一个类,将解析出来的结果保存到st_env_config结构中
#ifndef SHBK_COMMON_INICONFIG_H
#define SHBK_COMMON_INICONFIG_H
#include <string>
#include <stddef.h>
#include "configdef.h"
class Iniconfig
{
public:
Iniconfig();
~Iniconfig();
bool loadFile(const std::string&path);
const st_env_config& getconfig();
private:
//用来存储配置文件读取出来的具体参数
st_env_config _config;
//是否已经加载过了
bool isLoaded;
};
#endif
iniconfig.cpp
#include "iniconfig.h"
//一会儿使用cmake将third文件夹包含进来
#include <iniparser/iniparser.h>
#include <iniparser/dictionary.h>
Iniconfig::Iniconfig():isLoaded(false)
{
}
Iniconfig::~Iniconfig()
{
}
bool Iniconfig::loadFile(const std::string& path)
{
//需要定义一个字典ini指针
dictionary* ini = NULL;
//使用iniparser_load方法对ini文件进行解析,结果用字典来存
ini = iniparser_load(path.c_str());
if (ini==NULL)
{
printf("cannot parse file: %s\n", path.c_str());
return false;
}
//区段:字段,如果这个字段不存在那么就是默认值127.0.0.1
const char* ip = iniparser_getstring(ini, "database:ip", "127.0.0.1");
int port = iniparser_getint(ini, "database:port", 3306);
const char* user = iniparser_getstring(ini, "database:user", "root");
const char* pwd = iniparser_getstring(ini, "database:pwd", "123456");
const char* db = iniparser_getstring(ini, "database:db", "bike");
int sport = iniparser_getint(ini, "server:port", 9090);
//构造st_env_config结构体
_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;
}
const st_env_config& Iniconfig::getconfig()
{
return _config;
}
main.cpp
#include "iniconfig.h"
#include "configdef.h"
//后续执行命令:xx.exe ./conf/shared_bike.ini
int main(int argc,char** argv)
{
if(argc!=2)
{
printf("Please input shbk <config file path>!\n");
return -1;
}
Iniconfig config;
if(!config.loadFile(std::string(argv[1])))
{
printf("load %s is failed.\n",argv[1]);
return -2;
}
st_env_config conf_args = config.getconfig();
printf("[database] ip:%s port:%d user:%s\n",conf_args.db_ip.c_str(),conf_args.db_port,conf_args.db_user);
return 0;
}
iniparser解析数据的流程
1、创建字典
//定义一个字典ini指针
dictionary* ini = NULL;
2、加载数据
//使用iniparser_load方法对ini文件进行解析,结果用字典来存
ini = iniparser_load(path.c_str());
3、读取字典中的数据(可以将字典中的数据转存到一个类中进行保存)
//区段:字段,如果这个字段不存在那么就是默认值127.0.0.1
const char* ip = iniparser_getstring(ini, "database:ip", "127.0.0.1");
int port = iniparser_getint(ini, "database:port", 3306);
const char* user = iniparser_getstring(ini, "database:user", "root");
const char* pwd = iniparser_getstring(ini, "database:pwd", "123456");
const char* db = iniparser_getstring(ini, "database:db", "bike");
int sport = iniparser_getint(ini, "server:port", 9090);
什么是区段和字段呢?👇
cmake设计
要使用cmake来对这几个文件进行编译链接,所以来来看看俺的目录结构吧:
可以看到有两个CMakeLists.txt,里层的CMakeLists.txt是为了生成一个libcommon.a
的库,在这个库中实现了一个类Iniconfig
,它使用了iniparser对ini文件进行解析,并将结果存入到一个st_env_config类中,而外层CMakeLists.txt就是为了执行main函数
,将libcommon.a进行使用,最终看到解析ini文件之后的结果【很多注释都在cmake的语句中了,如果对于cmake不熟悉的同学可以仔细看看下面的注释】
和main函数同级的外层CMakeList.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(myBike)
# 包含头文件目录
INCLUDE_DIRECTORIES(./common)
INCLUDE_DIRECTORIES(../third/include)
# 包含库目录
LINK_DIRECTORIES(../third/lib/iniparser)
LINK_DIRECTORIES(./common)
# 用来显式的定义变量
# 设置编译选项
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic -Wall -m64 -pipe -std=c++0x -lrt -Wno-reorder -Wdeprecated-declarations")
# 收集指定目录下的文件名列表保存到变量中
aux_source_directory(. SOURCE_FILES)
ADD_EXECUTABLE(myBike ${SOURCE_FILES})
# 将具体的库链接上去
TARGET_LINK_LIBRARIES(myBike common)
TARGET_LINK_LIBRARIES(myBike iniparser)
# 执行common文件夹下的CMakeLists.txt(这个操作就是生成libcommon.a)
ADD_SUBDIRECTORy(common)
common文件夹中的里层CMakeList.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
# 收集指定目录下的文件名列表保存到变量中
aux_source_directory(. SOURCE_COMMON_FILES)
# 将这些个文件构建成一个库
ADD_LIBRARY(common ${SOURCE_COMMON_FILES})
# 用来显式的定义变量
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic -Wall -m64 -pipe -std=c++0x -lrt -Wno-reorder -Wdeprecated-declarations")
# 将指定目录添加到编译器的头文件搜索路径下
INCLUDE_DIRECTORIES(../../third/include)
# 将指定目录添加到需要链接的库文件目录之下
LINK_DIRECTORIES(../../third/lib/iniparser)
# 将iniparser的库链接到common上
TARGET_LINK_LIBRARIES(common iniparser)
# TARGET_LINK_LIBRARIES(common dl)
运行结果:
结语
在本文中,我们介绍了如何使用iniparser库在Linux中解析INI文件,并使用CMake来构建我们的项目。我们首先简单展示了INI文件的格式,然后演示了如何使用iniparser库来读取INI文件中的配置信息。接下来,我们学习了如何使用CMake来配置我们的项目,并编写了CMakeLists.txt文件。最后,我们演示了如何使用CMake来构建我们的项目,并成功地解析了INI文件中的配置信息。
通过本文,我希望读者能够了解如何在Linux中使用iniparser库来解析INI文件,并学习如何使用CMake来构建C++项目。在实际工作中,配置文件是非常常见的,而INI文件作为其中的一种格式也是非常重要的。因此,掌握使用iniparser库来解析INI文件的方法,将有助于我们更加高效地处理配置文件。此外,使用CMake来构建C++项目也是一个非常重要的技能,它可以帮助我们更好地管理和组织我们的代码。
感谢您的阅读,希望本文对您有所帮助!