版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1423468
在程序员的开发生涯中,读写配置文件必不可少。
配置文件有利于我们灵活配置工程,解决大量重复劳动,也方便调试。
配置文件的格式有很多,最简单的有一行一行的文本,也有像 json、xml、protocol buffer 这样结构化的格式,当然也有 yaml 这种格式。
今天的博文介绍的是如何在 C 开发中利用 yaml-cpp 开源库读写 yaml 配置文件。
如果有 Python 开发经验的同学,可能知道用 Python 读取 yaml 是再简单不过了,但是 C 麻烦一点,它需要你自己下载源码然后编译生成库文件。
yaml-cpp
yaml-cpp 是一个开源库,地址在 github 上,https://github.com/jbeder/yaml-cpp
yaml-cpp 是通过 CMake 来进行构建和编译的。
在这里假设读者都有 CMake 相关的经验,没有的同学自行百度。我的博文也写过比较简单的几篇,有兴趣的可以去看一看。
首先下载源码。
然后,在源码目录创建一个 build 文件夹。
代码语言:javascript复制mkdir build
进入到 build 文件夹,然后执行 cmake 命令。
代码语言:javascript复制cd build
cmake ..
注意的是 cmake 后面是 ..
,这代表从 build 上一层目录查找 CMakeLists.txt ,然后编译的文件都会存放在 build 文件夹,如果对编译的效果不满意,只要删除 build 文件就好了,其他源码目录并不受影响,这是 cmake 编译时的基本套路。
yaml-cpp 默认构建的就是静态库,也就是 unix 类系统下的 .a 文件,如果你想构建动态库的话,就需要在 cmake 时指定。
代码语言:javascript复制cmake .. -D BUILD_SHARED_LIBS=ON
编译成功后,会生成库文件,你只需要将库文件和头文件拷贝到你自己的工程当中,就可以使用了。
需要处理好头文件。
你如果不想每次都到 copy 头文件到不同的工程中,那么你可以将头文件 copy 到系统默认的头文件目录,比如 ubuntu 的地址是 /usr/local/include
,将库文件拷贝到系统默认的 lib 文件就好了,比如 ubuntu 是 /usr/local/lib
。
有了头文件和库,我们就可以顺利写代码了。
读取 yaml 配置文件
假设我们有这样一个配置文件
config.yaml
代码语言:javascript复制name: frank
sex: male
age: 18
skills:
c : 1
java: 1
android: 1
python: 1
温馨提示:yaml 中的内容,:后面一定要加空格哦
现在,我们的目标是要把它正确的读取出来。
yaml_test.cpp
代码语言:javascript复制#include <iostream>
#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<string>() << endl;
cout << "sex:" << config["sex"].as<string>() << endl;
cout << "age:" << config["age"].as<int>() << endl;
return 0;
}
头文件在 include 目录。
libs 存放 .so 文件。
然后通过 cmake 编译,因为我习惯用 cmake,如果读者喜欢用原始的 g 编译或者 makefile 也是可以的。
我的 CMakeFileLists.txt 如下:
代码语言:javascript复制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 操作。
代码语言:javascript复制mkdir build
cd build
cmake ..
最终生成了名为 yamltest 的可执行文件。
执行后,输出的信息如下。
代码语言:javascript复制name:frank
sex:male
age:18
可以看到,信息都被正常的读取出来了。
Node
Node 是 yaml-cpp 中的核心概念,它用于存储解析后的 yaml 信息。
生成 Node 的形式有很多种, loadFile() 是最常见的一种。
代码语言:javascript复制Node LoadFile(const std::string& filename)
filename 就是配置文件的路径。
有了 Node 之后,所有的信息都可以检索到。
比如 name.
代码语言:javascript复制cout << "name:" << config["name"].as<string>() << endl;
as<string>()
表示将解析的内容转换成 string 类型。
你也可以转换成其它类型。
它是一个模板方法。
有同学可能会有疑惑。
代码语言:javascript复制skills:
c : 1
java: 1
android: 1
python: 1
skills 的信息怎么读呢?
其实也非常简单。
代码语言:javascript复制cout << "skills c :" << config["skills"]["c "].as<int>() << endl;
cout << "skills java:" << config["skills"]["java"].as<int>() << endl;
cout << "skills android:" << config["skills"]["android"].as<int>() << endl;
cout << "skills python:" << config["skills"]["python"].as<int>() << endl;
yaml-cpp 中的迭代
yaml-cpp 中也可以通过迭代的方式,访问 Node 中的内容。
比如,访问 skills 下面的各个元素。
代码语言:javascript复制for(YAML::const_iterator it= config["skills"].begin(); it != config["skills"].end(); it)
{
cout << it->first.as<string>() << ":" << it->second.as<int>() << endl;
}
用 begin() 获取迭代器,用 end() 判断迭代器是否结束。
NodeType
yaml 支持 Scalar、List、Map 类型,yaml-cpp 通过 NodeType 定义了 Node 的可能类型。
代码语言:javascript复制namespace YAML {
struct NodeType {
enum value { Undefined, Null, Scalar, Sequence, Map };
};
}
对应未定义、空、标量、序列、字典。
代码语言:javascript复制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。
结果如下:
代码语言:javascript复制 Type: 3
Type: 2
Type: 4
分别对应 Sequence、Scalar、Map。
yaml-cpp 写配置文件
日常开发中,除了读取配置参数,我们经常需要保存参数,yaml-cpp 自然也提供了相应的功能。
代码语言:javascript复制ofstream fout("testconfig.xml");
config["score"] = 99;
fout << config;
fout.close();
前面代码解析成功的 config,现在添加一个 score,然后保存。
运行代码后,发现 build 文件夹下正确保存了 testconfig.xml 文件,score 被正确添加进去了。
代码语言:javascript复制name: frank
sex: male
age: 18
skills:
c : 1
java: 1
android: 1
python: 1
score: 99
到此,yaml-cpp 的简单使用就 OK 了,读者可以查看代码去深入学习。
本篇文章示例代码,目录结构如下图:
完整代码:
yaml_test.cpp
代码语言:javascript复制#include <iostream>
#include "include/yaml-cpp/yaml.h"
#include <fstream>
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<string>() << endl;
cout << "sex:" << config["sex"].as<string>() << endl;
cout << "age:" << config["age"].as<int>() << endl;
cout << "skills c :" << config["skills"]["c "].as<int>() << endl;
cout << "skills java:" << config["skills"]["java"].as<int>() << endl;
cout << "skills android:" << config["skills"]["android"].as<int>() << endl;
cout << "skills python:" << config["skills"]["python"].as<int>() << endl;
for(YAML::const_iterator it= config["skills"].begin(); it != config["skills"].end(); it)
{
cout << it->first.as<string>() << ":" << it->second.as<int>() << 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;
}