当前位置: 首页 > 工具软件 > yaml-cpp > 使用案例 >

利用 yaml-cpp 开源库读写 yaml 配置文件—— 读写部分

牟焱
2023-12-01

YAML文件解析

Node

Node 是 yaml-cpp 中的核心概念,是最重要的数据结构,它用于存储解析后的 yaml 信息。
Node一共有以下几种type:
1)Null 空节点
2)Sequence 序列,类似于一个Vector,对应YAML格式中的数组
3)Map 类似标准库中的Map,对应YAML格式中的对象
4)Scalar 标量,对应YAML格式中的常量

生成 Node 的形式有很多种,loadFile() 是最常见的一种。
Node LoadFile(const std::string& filename) ,其中filename 就是yaml文件的路径。

有了 Node 之后,所有的信息都可以检索到。比如 name.
cout << “name:” << config[“name”].as() << endl;其中as()表示将解析的内容转换成 string 类型,也可以转换成其它类型。

读写yaml文件

假设配置文件config.yaml

config.yaml内容

name: frank
sex: male
age: 18
skills: 
  c++: 1
  java: 1
  android: 1
  python: 1

注意:yaml 中的内容,: 后面一定要加空格

test_yaml.cpp文件内容

#include <iostream>
#include "yaml-cpp/yaml.h"
#include <fstream>
using namespace std;
int main(int argc,char** argv)
{
    YAML::Node config;
    try{
         config = YAML::LoadFile("../config.yaml");
    } catch(YAML::BadFile &e) {
        std::cout<<"read error"<<std::endl;
        return -1;
    }
    
    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;
 
    //读取不存在的node值,报YAML::TypedBadConversion异常
    try{
        string label = config["label"].as<string>();
    }catch(YAML::TypedBadConversion<string> &e){
        std::cout<<"label node is NULL"<<std::endl;
    }//TypedBadConversion是模板类,读取什么类型的参数就传入什么类型
 
    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;
    //通过迭代的方式,访问 skills 下面的各个元素
    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;
    //将score 写入./testconfig.yaml
    ofstream fout("./testconfig.yaml");
    config["score"] = 99;//添加新元素
    fout << config;
    fout.close();
    return 0;
}

重要内容简单解释

1)Node
Node 是 yaml-cpp 中的核心概念,它用于存储解析后的 yaml 信息。
生成 Node 的形式有很多种, loadFile() 是最常见的一种。

Node LoadFile(const std::string& filename)

filename 就是配置文件的路径。
有了 Node 之后,所有的信息都可以检索到。

2)skills 的信息怎么读呢?

skills:  
  c++: 1
  java: 1
  android: 1
  python: 1

其实也非常简单

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 中也可以通过迭代的方式,访问 Node 中的内容
比如,访问 skills 下面的各个元素

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() 判断迭代器是否结束。

3)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。

4)yaml-cpp 写配置文件
日常开发中,除了读取配置参数,我们经常需要保存参数,yaml-cpp也提供了相应的功能。

ofstream fout("./testconfig.yaml");
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

5)总结:
std::ifstream file(“config.yaml”);
YAML::Node node = YAML::Load(file);//读取来自test.yaml的node文件YAML
std::cout << node <<std::endl;
//或者
YAML::Node node_2 = YAML::LoadFile(“config.yaml”);//也可以这样读取文件
std::cout << node_2[“node_2”] <<std::endl;//可以直接用下标访问
for(auto it = node_2.begin(); it != node_2.end(); it++)
std::cout << it->first << it->second << std::endl;//也可以用迭代器访问

node的增改查删

#include <fstream>
#include <yaml-cpp/yaml.h>
#include <iostream>
#include <assert.h>
 
int main()
{
    YAML::Node node;  
    assert(node.IsNull());  //初始化的节点是Null类型
    node["key"] = "value";  
    //node.force_insert("key", "value");//这个操作和上面等价,但是它不会检查是否存在"key"键,不推荐使用
    if(node["mascot"])
        std::cout << node["mascot"].as<std::string>() << "\n";//单纯的查询操作不会增加一个key,当然上面的if不会执行
 
    node["number"] = 255;
    assert(node.IsMap());   //node是一个Map
    node["seq"].push_back("first element");
    node["seq"].push_back("second element");//node的seq下是Sequence类型,有两个参数
 
    YAML::Node node_2;  
    node_2.push_back("first_item");//如果不给node_2键值对,它是一个sequence类型
    node_2.push_back("second_item");
    node_2.push_back("third_item");
    std::vector<int> v = {1,3,5,7,9};//给node_2插入了一个Sequence
    node_2.push_back(v);
    assert(node_2.IsSequence());//node_2仍然是一个Sequence
 
    assert(node_2[0].as<std::string>() == "first item");
    //对于Sequence类型,你可以使用它的下标来访问
    //注意这里as<T>是一个模板转换,node_2[0]的type是NodeType::Scalar
    auto it = node_2.begin();
    for(; it != node_2.end(); it++)
        std::cout << *(it) << std::endl;
    //当然,可以用迭代器来访问
    //它们的类型分别是NodeType::Scalar,NodeType::Scalar,NodeType::Scalar,NodeType::Sequence
    //取值时记得使用as进行模板转换
    node_2["key"] = "value";
    assert(node_2.IsMap());//一旦node_2接收到键值对,它转变为Map类型
    assert(node_2[0].as<std::string>() == "first item");//此时,Sequence时的下标变为它的key值
    node["node_2"] = node_2;//将node_2作为node的一个子项
    node["pointer_to_first_element"] = node["seq"][0];//也可以给已有的node设置一个别名,类似于一个指针
    assert(node["pointer_to_first_element"].as<std::string>() == "first element");//可以通过这个指针访问那个node
 
    //node.remove(node["seq"][0]);//可以通过指定一个node来删除它
    //node.remove("pointer_to_first_element");//也可以通过指定key来删除它
	std::cout << node << endl;
}

最后cout的结果如下:

key: value
number: 255
seq:
  - first element
  - second element
node_2:
  0: first item
  1: second_item
  2: third_item
  3:
    - 1
    - 3
    - 5
    - 7
    - 9
  key: value

注:如有侵权,请联系删除!

 类似资料: