在程序员的开发生涯中linux系统界面,读写配置文件必不可少。
配置文件有利于我们灵活配置工程,解决大量重复劳动,也便捷调试。
配置文件的格式有好多,最简单的有一行一行的文本,也有像json、xml、protocolbuffer这样结构化的格式,其实也有yaml这些格式。
明天的博文介绍的是怎样在C++开发中借助yaml-cpp开源库读写yaml配置文件。
假如有Python开发经验的朋友,可能晓得用Python读取yaml是再简单不过了,而且C++麻烦一点,它须要你自己下载源码之后编译生成库文件。
yaml-cpp
yaml-cpp是一个开源库,地址在github上,
yaml-cpp是通过CMake来进行完善和编译的。
在这儿假定读者都有CMake相关的经验,没有的朋友自行百度。我的博文也写过比较简单的几篇,有兴趣的可以去看一看。
首先下载源码。
之后,在源码目录创建一个build文件夹。
mkdir build
步入到build文件夹linux find -name 写入文件红旗linux系统下载,之后执行cmake命令。
cd build
cmake ..
注意的是cmake旁边是..,这代表从build上一层目录查找CMakeLists.txt,之后编译的文件还会储存在build文件夹,假如对编译的疗效不满意,只要删掉build文件就好了,其他源码目录并不受影响,这是cmake编译时的基本套路。
yaml-cpp默认建立的就是静态库,也就是unix类系统下的.a文件,假如你想建立动态库的话,就须要在cmake时指定。
cmake .. -D BUILD_SHARED_LIBS=ON
编译成功后,会生成库文件,你只须要将库文件和头文件拷贝到你自己的工程当中,就可以使用了。
须要处理好头文件。
你若果不想每次都到copy头文件到不同的工程中,这么你可以将头文件copy到系统默认的头文件目录,例如ubuntu的地址是/usr/local/include,将库文件拷贝到系统默认的lib文件就好了,例如ubuntu是/usr/local/lib。
有了头文件和库,我们就可以顺利写代码了。
读取yaml配置文件
假定我们有这样一个配置文件
config.yaml
name: frank
sex: male
age: 18
skills:
c++: 1
java: 1
android: 1
python: 1
温情提示:yaml中的内容,:前面一定要加空格哦
如今,我们的目标是要把它正确的读取下来。
yaml_test.cpp
#include
#include "include/yaml-cpp/yaml.h"
using namespace std;
int main(int argc,char** argv)
{
YAML::Node config = YAML::LoadFile("../config.yaml");
cout << "name:" << config["name"].as() << endl;
cout << "sex:" << config["sex"].as() << endl;
cout << "age:" << config["age"].as() << endl;
return 0;
}
头文件在include目录。
libs储存.so文件。
之后通过cmake编译,由于我习惯用cmake,假如读者喜欢用原始的g++编译或则makefile也是可以的。
我的CMakeFileLists.txt如下:
cmake_minimum_required(VERSION 3.2)
project(yaml_test)
add_definitions(-std=c++11)
include_directories(include)
set(SRCS yaml_test.cpp)
add_executable(yamltest ${SRCS})
target_link_libraries(yamltest ${CMAKE_HOME_DIRECTORY}/libs/libyaml-cpp.so)
在当前目录创建build文件夹,之后步入build文件执行cmake操作。
mkdir build
cd build
cmake ..
最终生成了名为yamltest的可执行文件。
执行后,输出的信息如下。
name:frank
sex:male
age:18
可以看见,信息都被正常的读取下来了。
Node
Node是yaml-cpp中的核心概念,它用于储存解析后的yaml信息。
生成Node的方式有好多种,loadFile()是最常见的一种。
Node LoadFile(const std::string& filename)
filename就是配置文件的路径。
有了Node以后,所有的信息都可以检索到。
例如name.
cout << "name:" << config["name"].as() << endl;
as()表示将解析的内容转换成string类型。
你也可以转换成其它类型。
它是一个模板方式。
有朋友可能会有疑虑。
skills:
c++: 1
java: 1
android: 1
python: 1
skills的信息怎样读呢?
虽然也十分简单。
cout << "skills c++:" << config["skills"]["c++"].as() << endl;
cout << "skills java:" << config["skills"]["java"].as() << endl;
cout << "skills android:" << config["skills"]["android"].as() << endl;
cout << "skills python:" << config["skills"]["python"].as() << endl;
yaml-cpp中的迭代
yaml-cpp中也可以通过迭代的方法,访问Node中的内容。
例如,访问skills下边的各个元素。
for(YAML::const_iterator it= config["skills"].begin(); it != config["skills"].end();++it)
{
cout <first.as() << ":" <second.as() << endl;
}
用begin()获取迭代器,用end()判定迭代器是否结束。
NodeType
yaml支持Scalar、List、Map类型,yaml-cpp通过NodeType定义了Node的可能类型。
namespace YAML {
struct NodeType {
enum value { Undefined, Null, Scalar, Sequence, Map };
};
}
对应未定义、空、标量、序列、字典。
YAML::Node test1 = YAML::Load("[1,2,3,4]");
cout << " Type: " << test1.Type() << endl;
YAML::Node test2 = YAML::Load("1");
cout << " Type: " << test2.Type() << endl;
YAML::Node test3 = YAML::Load("{'id':1,'degree':'senior'}");
cout << " Type: " << test3.Type() << endl;
里面的代码是为了判定NodeType。
结果如下:
Type: 3
Type: 2
Type: 4
分别对应Sequence、Scalar、Map。
yaml-cpp写配置文件
日常开发中linux find -name 写入文件,不仅读取配置参数,我们常常须要保存参数,yaml-cpp自然也提供了相应的功能。
ofstream fout("testconfig.xml");
config["score"] = 99;
fout << config;
fout.close();
后面代码解析成功的config,如今添加一个score,之后保存。
运行代码后,发觉build文件夹下正确保存了testconfig.xml文件,score被正确添加进去了。
name: frank
sex: male
age: 18
skills:
c++: 1
java: 1
android: 1
python: 1
score: 99
到此,yaml-cpp的简单使用就OK了,读者可以查看代码去深入学习。
本篇文章示例代码,目录结构如右图:
完整代码:
yaml_test.cpp
#include
#include "include/yaml-cpp/yaml.h"
#include
using namespace std;
int main(int argc,char** argv)
{
YAML::Node config = YAML::LoadFile("../config.yaml");
cout << "Node type " << config.Type() << endl;
cout << "skills type " << config["skills"].Type() << endl;
cout << "name:" << config["name"].as() << endl;
cout << "sex:" << config["sex"].as() << endl;
cout << "age:" << config["age"].as() << endl;
cout << "skills c++:" << config["skills"]["c++"].as() << endl;
cout << "skills java:" << config["skills"]["java"].as() << endl;
cout << "skills android:" << config["skills"]["android"].as() << endl;
cout << "skills python:" << config["skills"]["python"].as() << endl;
for(YAML::const_iterator it= config["skills"].begin(); it != config["skills"].end();++it)
{
cout <first.as() << ":" <second.as() << endl;
}
YAML::Node test1 = YAML::Load("[1,2,3,4]");
cout << " Type: " << test1.Type() << endl;
YAML::Node test2 = YAML::Load("1");
cout << " Type: " << test2.Type() << endl;
YAML::Node test3 = YAML::Load("{'id':1,'degree':'senior'}");
cout << " Type: " << test3.Type() << endl;
ofstream fout("testconfig.xml");
config["score"] = 99;
fout << config;
fout.close();
return 0;
}