c++对YAML配置文件的操作方法

1 下载安装YAML开源库方法1:方法2:

2 使用visual studio 2017 链接YAML1 新建工程2 更改工程项目属性3 进行文件包含

3 使用YAML进行 “增删改查” 的基本操作1 为方便操作,新建一个配置文件2 增 操作3 删 操作4 改 操作5 查 操作5.1 数据类型5.2 查询数据5.3 空值处理5.4 常见错误

结论

1 下载安装YAML开源库

方法1:

YAML是一个开源库,源代码可以在GitHub下载到,链接:

https://github.com/jbeder/yaml-cpp

yaml-cpp 是通过 CMake 来进行构建和编译的。使用CMake的方法可以自行百度,网上

一大堆。

方法2:

直接百度网盘下载我这里构建好的,包含64位和32位两个版本。 附上链接: 链接:https://pan.baidu.com/s/1_QYPX2IpP7UWlEteRJCeqw 提取码:ehmp 嫌麻烦的或者没有CMake操作经验的,可以直接百度网盘下载对应文件。

2 使用visual studio 2017 链接YAML

1 新建工程

首先根据自己需要下载一个32位或者64位的控制台程序。将下载好或者自行编译好的文件夹放到自己的工程目录下面。为了操作方便,我这里直接放到了了和 .sln 同一级的目录下。

2 更改工程项目属性

用vs打开工程之后,点击上方工具栏中的项目,找到最下方的项目属性。分别在属性页中的C/C++:常规。链接器:常规和输入完成相关配置。配置结果如下。

3 进行文件包含

使用过C/C++编程的人都知道,在使用某个库之前,我们需要包含它的头文件,然后才能使用里面的函数。在上面链接好工程之后,在main.cpp里面包含YAML库的头文件,如果能包含成功,则说明配置无误,若包含失败,请检查上面操作步骤。

3 使用YAML进行 “增删改查” 的基本操作

完成操作之后,我们开始文件的基本操作 “增删改查”。 一般来说,我们用C/C++进行文件操作时,需要不断偏移文件的指针去解析自己想要的内容。在YAML操作这里,首先让我们先忘掉指针操作。

1 为方便操作,新建一个配置文件

name: dong

number: 1

age:

skills:

eat: 2

drink: 3

play: 4

study: 5

self: 99

这里有一点需要注意,YAML是一个严格按照空格进行区分节和子节的一种格式语言。若不在同一缩进的格式下,计时在同一个节下面,也会将其分为不同子节。下面验证一下。

2 增 操作

YAML文件的增加操作,目前我喜欢用两种方法,分别为 push_back方法和数组操作方法,下面用一段代码分别介绍。

YAML::Node config; //< 创建节点

config.IsNull(); //< 初始化节点

ofstream fout("../dong1.yaml"); //< C++ stream 读取文件

config["123"] = 456; //< 插入格式 1 直接插入第一节

config["456"].push_back(789); //< 插入格式2 插入第一节

fout << config;

fout.close();

这里可以看一下两者放入方法的不同区别:贴图如下: 可以看到,用第一种方法插入的直接对键进行了赋值,第二种方法,则是在键下面新建了一个 “标量”。可以根据需要来决定采取何种插入方法。这里可能会有人有疑问:如果我想要在某一个子节下面增加数据,该如何操作。下面贴上代码:

YAML::Node config; //< 创建节点

config.IsNull(); //< 初始化节点

ofstream fout("../dong1.yaml"); //< C++ stream 读取文件

config["123"]["456"] = 456; //< 插入格式 1 直接插入第一节

config["456"].push_back(789); //< 插入格式2 插入第一节

fout << config;

fout.close();

只需要在后面新加一个中括号即可解决问题。

3 删 操作

YAML文件的删除需要用到 remove函数。该函数有两种用法,下面用一个例子来分别介绍。 源数据:

name: dong

number: 1

skills:

eat: 2

drink: 3

play: 4

study: 5

self: 99

remove的用法:

//< 删

YAML::Node config = YAML::LoadFile("../dong.yaml");

config["skills"].remove("eat");//< 1、删除skills下面的eat元素

config.remove("number");//< 2、通过指定key来删除

ofstream fout("../dong2.yaml"); //< C++ stream 读写文件

fout << config;

fout.close();

运行完之后的结果如下图:

可以看到已经将所指出的饿两个文件删除掉了

4 改 操作

改操作和上面的增操作类似,增操作是新增一个不存在的键或者节。改的操作只需要将其修改为已经存在的值,具体实现如下:

//< 改

YAML::Node config = YAML::LoadFile("../dong.yaml");

ofstream fout("../dong1.yaml"); //< C++ stream 读取文件

config["skills"]["eat"] = 10; //< 修改格式 1

config["name"] = "hening"; //< 修改格式2

fout << config;

fout.close();

可以看到,上面分别修改了两个点的内容,修改结果如下所示: 可以看到,修改操作和插入操作基本相同。

5 查 操作

查询操作基本上是我们用到的最多的操作了,了解到了以上三种 增、删、改的操作,下面的查询操作基本上就是小菜一碟了,下面首先介绍一下yaml的几种数据类型。

5.1 数据类型

//分别为:未定义、空、标量、序列、字典。

enum value { Undefined, Null, Scalar, Sequence, Map };

用代码验证一下上面:

YAML::Node test1 = YAML::Load("[1,2,3,4]");

cout << " Type: " << test1.Type() << endl; //< 3

YAML::Node test2 = YAML::Load("1");

cout << " Type: " << test2.Type() << endl; //< 2

YAML::Node test3 = YAML::Load("{'first':'dong' 'last':'hening'}");

cout << " Type: " << test3.Type() << endl; //< 4

分别对应位 序列、标量以及字典:

5.2 查询数据

依旧是读取dong.yaml文件的内容。 基本查询,首先查询节的内容,不包括子节。

在这里插入代码片 YAML::Node config = YAML::LoadFile("../dong.yaml");

for (auto it : config) {

cout << it.first.as() << endl; //< 指定数据类型

}

//< 这里使用 auto 可能不好理解,下面给出基本迭代器的使用

for (YAML::const_iterator it = config.begin(); it != config.end(); it++){

cout << it->first.as() << endl;

}

这里使用迭代器的方法进行查询,查询到的结果需要为其指定数据类型后输出。 运行结果如下: 如果我们想要查询子键的内容该如何去操作,基本上和上面的操作相同,只需要在中括号内为其指定即可:

YAML::Node config = YAML::LoadFile("../dong.yaml");

for (auto it : config["skills"]) {

cout << it.first.as() << ":" << it.second.as() << endl;

}

运行结果:

5.3 空值处理

这里有一点需要注意,我这里采用了second来读取了该键所对应的值,若此键没有设置值该怎么办? 下面我把eat所对应的2删除掉,运行一遍,给出结果: 他为我们自动填充了null类型,此时我们若要采用此值就需要判断其数据类型。

YAML::Node config = YAML::LoadFile("../dong.yaml");

for (auto it : config["skills"]) {

YAML::Node test2 = YAML::Load(it.second.as());

cout << " Type: " << test2.Type() << endl; //< 2

cout << it.first.as() << ":" << it.second.as() << endl;

}

可以看到运行结果,判断类型为空。可以根据判断的类型做特殊处理。

5.4 常见错误

这里我们修改文件如下: 可以看到,我们给skills设置了值,这里看一下运行结果: 直接报出异常!结论显而易见。不能再一个键值对下面设置子键。对于节和键要区分开来。

结论

贴上完整代码:

#include

#include "yaml-cpp/yaml.h"

#include

#include

using namespace std;

//分别为:未定义、空、标量、序列、字典。

//enum value { Undefined, Null, Scalar, Sequence, Map };

int main(){

//< 增

//YAML::Node config; //< 创建节点

//config.IsNull(); //< 初始化节点

//ofstream fout("../dong1.yaml"); //< C++ stream 读取文件

//config["123"]["456"] = 456; //< 插入格式 1 直接插入第一节

//config["456"].push_back(789); //< 插入格式2 插入第一节

//fout << config;

//fout.close();

//< 删

//YAML::Node config = YAML::LoadFile("../dong.yaml");

//config["skills"].remove("eat");//你可以通过指定一个node来删除它

//config.remove("number");//你也可以通过指定key来删除它

//ofstream fout("../dong2.yaml"); //< C++ stream 读取文件

//fout << config;

//fout.close();

< 改

//YAML::Node config = YAML::LoadFile("../dong.yaml");

//ofstream fout("../dong1.yaml"); //< C++ stream 读取文件

//config["skills"]["eat"] = 10; //< 修改格式1

//config["name"] = "hening"; //< 修改格式2

//fout << config;

//fout.close();

< 查

//

///< Type Judge link to row 112 to 113

//YAML::Node test1 = YAML::Load("[1,2,3,4]");

//cout << " Type: " << test1.Type() << endl; //< 3

//YAML::Node test2 = YAML::Load("1");

//cout << " Type: " << test2.Type() << endl; //< 2

//YAML::Node test3 = YAML::Load("{'id':1 'degree':'senior'}");

//cout << " Type: " << test3.Type() << endl; //< 4

YAML::Node config = YAML::LoadFile("../dong.yaml");

for (auto it : config["skills"]) {

YAML::Node test2 = YAML::Load(it.second.as());

cout << " Type: " << test2.Type() << endl; //< 2

cout << it.first.as() << ":" << it.second.as() << endl;

}

for (YAML::const_iterator it = config.begin(); it != config.end(); it++)

{

cout << it->first.as() << endl;

}

system("pause");

return 0;

}

查看原文