【C++】利用yaml-cpp读写yaml配置文件

参考:yaml-cpp介绍

1. YAML简介

1.1 定义规则

YAML是专门用来写配置文件的语言,非常简洁和强大,比JSONxml格式要方便很多。
YAML语言(发音 /ˈjæməl/ )的设计目标,就是方便人类读写,它实质上是一种通用的数据串行化格式

它的基本语法规则如下:

 - key: value;kv之间有空格 k: v
 - 大小写敏感 
 - 使用缩进表示层级关系 
 - 缩进时不允许使用Tab键,只允许使用空格。 
 - 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可  
 - #表示注释,从这个字符一直到行尾,都会被解析器忽略。
 - 字符串默认不使用引号表示。如果字符串之中包含空格或特殊字符,需要放在引号之中。 
 - 单引号和双引号都可以使用,双引号不会对特殊字符转义。
 - 单引号之中如果还有单引号,必须连续使用两个单引号转义。
 - 字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。

YAML支持的数据结构有三种:

 - 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
 - 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
 - 纯量(scalars):单个的、不可再分的值

在这里插入图片描述

1.2 常用解析库

一般常用yaml-cppOpenCV进行解析。

相比yaml-cppOpenCV优点是可以在YAML文件中存储矩阵,读出来就是cv::Mat格式;缺点OpenCV要求YAML文件有一个特殊的头,与标准的YAML文件并不兼容。或者也可以理解为OpenCV定义了一种新的YAML格式。

2. 安装yaml-cpp

git clone https://github.com/jbeder/yaml-cpp.git
cd yaml-cpp
mkdir build && cd build
cmake .. && make -j
sudo make install

3. CMakeLists配置

find_package(yaml-cpp REQUIRED)
include_directories(${YAML_CPP_INCLUDE_DIR})
target_link_libraries(node_name yaml-cpp)

4. yaml的解析

4.1 Node

Nodeyaml-cpp中的核心概念,是最重要的数据结构,它用于存储解析后的yaml信息。

Node一共有以下几种type:
- Null 空节点
- Sequence 序列,类似于一个Vector,对应YAML格式中的数组
- Map,类似标准库中的Map,对应YAML格式中的对象
- Scalar 标量,对应YAML格式中的常量

生成 Node 的形式有很多种, loadFile() 是最常见的一种:

Node LoadFile(const std::string& filename)

其中filename 就是yaml文件的路径。

有了 Node 之后,所有的信息都可以检索到。比如 name

cout << "name:" << config["name"].as<string>() << endl;

as<string>()表示将解析的内容转换成 string 类型,你也可以转换成其它类型,它是一个模板方法。

4.2 yaml文件的解析

比如这样一个配置文件config.yaml

name: frank
sex: male
age: 18
 
skills: 
  c++: 1
  java: 1
  android: 1
  python: 1
#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;
 
    //可以用string类型作为下表,读取参数
    string age = "age";
    cout << "age when string is label:" << config[age].as<int>() << 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;
 
    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.yaml"); //保存config为yaml文件
 
    config["score"] = 99;//添加新元素
 
    fout << config;
 
    fout.close();
 
    return 0;
}
4.3 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";  //当你给它赋值键值对,它转变为Map类型
    //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的一个子项
	
	std::cout << node << endl;
    
    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来删除它
}

结果:

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
4.4 yamlcpp中的迭代

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

4.5 NodeType类型

yaml 支持 ScalarListMap 类型,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;

结果:

 Type: 3
 Type: 2
 Type: 4

分别对应:Sequence、Scalar、Map。

4.6 yaml-cpp写配置文件(yaml文件的保存与读取)

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

Node可以使用文件流的方式进行读写,前面已经使用过了,保存一个node可以用下面的方法:

std::ofstream fout("config.yaml");
...//设置配置文件node数据
fout << node <<std::endl;
 
fout.close();

这样,上面打印到cout的内容会被输出到config.yaml文件。

为了读取一个node,你可以这么做:

std::ifstream file("config.yaml");
YAML::Node node = YAML::Load(file);//读取来自test.yaml的node文件
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;//也可以用迭代器访问
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Qt是一个跨平台的C++应用程序开发框架,而yaml-cpp是一个用于解析和生成YAML格式的C++库。当在Qt项目中需要使用YAML格式来存储或加载配置文件、数据序列化等时,可以选择使用yaml-cpp库。 yaml-cpp库提供了简单易用的接口,使开发人员可以轻松地解析和生成YAML数据。它支持基本的YAML标量类型,如字符串、整数、浮点数,以及容器类型如序列和映射。在Qt项目中,可以利用这些功能来方便地读取和写入YAML格式的数据。 使用yaml-cpp库与Qt的整合也是相对简单的。首先,需要将yaml-cpp库的源代码添加到Qt项目中,可以通过下载源码并在项目中添加相应的文件来完成。然后,可以使用Qt的信号和槽机制来将yaml-cpp的解析结果传递给其他Qt组件,或者将Qt组件的数据传递给yaml-cpp库进行生成YAML数据。 在使用qt yaml-cpp的过程中,可以遵循以下几个步骤: 1. 引入yaml-cpp库的头文件 2. 创建一个YAML文档对象,用于解析或生成YAML数据 3. 调用yaml-cpp的接口来读取或写入YAML数据 4. 使用Qt的信号和槽机制来处理解析结果或将数据传递给其他组件 使用qt yaml-cpp可以方便地在Qt项目中进行YAML格式的读写操作。无论是配置文件还是数据序列化,都可以通过qt yaml-cpp来实现。这为Qt开发人员带来了更多的选择和灵活性,使他们能够更好地应对不同的开发需求。 ### 回答2: Qt是一种流行的跨平台应用程序开发框架,使用C++语言编写。它提供了丰富的功能和工具,用于开发各种类型的应用程序,包括桌面应用程序、移动应用程序和嵌入式系统应用程序。Qt框架具有良好的可扩展性,可以满足不同项目的需求。 而yaml-cpp是一个用于解析和生成YAML格式文件的C++库。YAMLYAML Ain't Markup Language)是一种人类可读的数据序列化格式,广泛应用于配置文件和数据交换。yaml-cpp库提供了简洁的接口,可以方便地读取和写入YAML文件。 Qt和yaml-cpp可以很好地结合使用。通过使用yaml-cpp库,我们可以在Qt应用程序中轻松地读取和写入YAML配置文件。这对于需要保存和加载应用程序设置、参数和其他数据的应用程序非常有用。yaml-cpp提供了简单的API,使得在Qt应用程序中解析和生成YAML文件变得容易。 在Qt应用程序中使用yaml-cpp库,通常需要将yaml-cpp库添加到项目的依赖项中。然后,我们可以使用yaml-cpp提供的API来读取和写入YAML文件。例如,我们可以使用yaml-cpp来读取配置文件中的参数,并将其应用到Qt应用程序中的各个组件;或者将Qt应用程序中的数据保存到YAML文件中,以便下次启动时加载。 综上所述,Qt和yaml-cpp可以很好地协同工作,使得在Qt应用程序中处理YAML文件变得非常方便。它们的结合可以极大地简化应用程序的配置和数据交换过程,提高开发效率。 ### 回答3: Qt是一款跨平台的C++应用程序开发框架,而yaml-cpp是一个用于解析和生成YAML文件的C++库。 Qt提供了丰富的功能和工具,使开发人员能够快速构建高效、交互性强的应用程序。它包括用于图形界面、网络通信、多媒体处理等方面的模块。通过使用Qt,开发人员可以更容易地开发出符合用户需求的应用程序。 而yaml-cpp是一个用于处理YAML文件的库。YAML是一种人类可读的数据序列化格式,适用于各种编程语言。yaml-cpp库提供了用于将YAML文件解析为C++对象或将C++对象序列化为YAML文件的功能。通过使用yaml-cpp,开发人员可以轻松地读写YAML文件,实现数据的存储和交换。 Qt和yaml-cpp可以很好地结合使用,以实现更强大和更灵活的应用程序功能。开发人员可以使用yaml-cpp库来加载和保存Qt应用程序的配置文件,以便配置应用程序的行为。同时,可以使用yaml-cpp库来解析并处理从网络上接收到的YAML数据,实现与其他系统的数据交换。 总而言之,Qt是一个功能丰富的C++应用程序开发框架,而yaml-cpp是一个用于解析和生成YAML文件的C++库。通过结合使用这两个工具,开发人员可以更容易地开发出高效、交互性强的应用程序,并实现数据的存储和交换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值