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

[C++]-yml库yaml-cpp简介

闻人景澄
2023-12-01


yaml-cpp是一个yml操作库。

YAML

YAML (YAML Ain’t a Markup Language,YAML不是一种标记语言)通常以.yml为后缀,是一种直观的能够被电脑识别的数据序列化格式,并且容易被人类阅读。

基本语法

yaml语法简单,适合做配置文件:

  • 大小写敏感;
  • 使用缩进表示层级关系;
    • 缩进不允许使用tab,只允许空格
    • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释;

数据类型

YAML支持以下几种数据类型:

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

对象

对象键值对使用冒号结构表示 key: value,冒号后面要加一个空格

多层对象可表示为:

key: {key1: value1, key2: value2}

或者

key:
  key1: value1
  key2: value2

对于复杂的对象,可使用问号加一个空格代表一个复杂的 key,配合一个冒号加一个空格代表一个 value。以键为[complexkey1,complexkey2],值为 [complexvalue1,complexvalue2]为例:

?  
    - complexkey1
    - complexkey2
:
    - complexvalue1
    - complexvalue2

数组

- 开头的行表示构成一个数组,以一个数组对象为例:

companies:
    -
        id: 1
        name: company1
        price: 200W
    -
        id: 2
        name: company2
        price: 500W

也可以下方式表示:

companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]

标量

标量是最基本的,不可再分的值,包括:

  • 字符串:有特殊字符(如空格等),可用单引号或双引号括起来;否则不需要;字符串可以拆成多行,换行符会转换为空格。
  • 布尔值:TRUE/true/True或FALSE/false/False;
  • 整数
  • 浮点数
  • Null:使用**~**表示null;
  • 日期、时间:必须是ISO 8601格式的;

引用

& 用来建立锚点(defaults),<< 表示合并到当前数据,***** 用来引用锚点。

合并数据示例:

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

等价于:

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

单纯引用示例:

localhost: 
	host: &host 127.0.0.1
	
user:
	host: *host
	db: users

等价于:

localhost: 
	host: 127.0.0.1
	
user:
	host: 127.0.0.1
	db: users

yaml-cpp库

yaml-cpp是C++的开源库(https://github.com/jbeder/yaml-cpp);

生成器Emitter

YAML::Emitter可用于生成YAML格式文本,通过c_str()获取对应字符串:

  • YAML::BeginSeq ... YAML::EndSeq:生成数组序列,其中间的内容即为数组元素;通过YAML::Flow可控制显示方式;
  • YAML::BeginMap ... YAML::EndMap:生成对象:
    • YAML::Key:用于输入键;
    • YAML::Value:用于输入值;
  • YAML::Anchor("AnchorName") ... YAML::Alias("AnchorName"):分别用于生成锚点,和引用锚点。

Emitter还可直接输入容器(如vector作为数组)。

void buildYAML() {
    YAML::Emitter out;
    out << YAML::BeginMap;
    out << YAML::Key << "name" << YAML::Value << "orchard";
    out << YAML::Key << "fruits";
    out << YAML::Value << YAML::BeginSeq << "apple" << "banana" << "pear" << YAML::EndSeq;

    std::vector<int> vec{10, 20, 30};
    out << YAML::Key << "weight" << YAML::Value << YAML::Flow << vec ;

    std::map<std::string, int> others;
    out << YAML::Key << "extra" << YAML::Value  << YAML::Block;
    others["pear"] = 10;
    others["peach"] = 20;
    out << others;

    out << YAML::EndMap;
    std::cout << out.c_str() << std::endl;
}
// name: orchard
// fruits:
//   - apple
//   - banana
//   - pear
// weight: [10, 20, 30]
// extra:
//   peach: 20
//   pear: 10

节点Node

Node是yaml-cpp中核心概念,用于存储解析后的yaml信息:

  • Node LoadFile(const std::string& filename):从文件中加载;
  • Node Load(...):从字符串或输入流中加载;

Node是一个类map结构,可通过operator["name"]来获取子项(可级联,且不存在时不会出错):

  • .IsDefined():判断是否存在。
  • .as<type>():获取对应值。
  • .Type():获取对应的类型({ Undefined, Null, Scalar, Sequence, Map });

数组

数组可像STL中的列表一样访问:

YAML::Node primes = YAML::Load("[2, 3, 5, 7, 11]");
for (std::size_t i=0;i<primes.size();i++) {
  std::cout << primes[i].as<int>() << "\n";
}
// or:
for (YAML::const_iterator it=primes.begin();it!=primes.end();++it) {
  std::cout << it->as<int>() << "\n";
}
// or:
for (auto num : primes) {
  std::cout << num.as<int>() << "\n";
}

对象

对象可像STL中的map一样访问:

YAML::Node lineup = YAML::Load("{1B: Prince Fielder, 2B: Rickie Weeks, LF: Ryan Braun}");
for(YAML::const_iterator it=lineup.begin();it!=lineup.end();++it) {
  std::cout << "Playing at " << it->first.as<std::string>() << " is " << it->second.as<std::string>() << "\n";
}
// or:
for(auto ln : lineup){
	std::cout << "Playing at " << ln.first.as<std::string>() << " is " << ln.second.as<std::string>() << "\n";
}

创建

通过Node也可方便地创建yaml:

void buildByNode() {
    YAML::Node node;  // starts out as null
    node["key"] = "value";  // it now is a map node
    node["seq"].push_back("first element");  // node["seq"] automatically becomes a sequence
    node["seq"].push_back("second element");

    node["mirror"] = node["seq"][0];  // this creates an alias

    auto strOut = YAML::Dump(node);
    std::cout<<strOut<<std::endl;
}

// key: value
// seq:
//   - &1 first element
//   - second element
// mirror: *1

解析

以读取config.yml为例:

common:
  logLevel: 1
  logPath: './logs'
  
  abilities:
    -
      name: motion
      ability: [run]
    -
      name: study
      ability: [math, english]
	  
plugin:
  file: ./plugin/fast.so
  param: 
    param1: value1
    p2: v2
    p3: test	

读取代码:

void loadConfigs() {
    YAML::Node root;
    try {
        root = YAML::LoadFile("./.config.yml");
    } catch (YAML::ParserException &ex) {
        std::cerr << "!! config parse failed: " << ex.what() << std::endl;
        exit(-1);
    } catch (YAML::BadFile &ex) {
        std::cerr << "!! config load failed: " << ex.what() << std::endl;
        exit(-1);
    }
    
    // common node:
    auto nodeCommon = root["common"];
    if (!nodeCommon.IsDefined()) {
        std::cerr << "Node [common] not found, use default log-config" << std::endl;
        return;
    }
    if (nodeCommon["logLevel"].IsDefined()) {
        std::cout << "LogLevel: " << nodeCommon["logLevel"].as<int>() << std::endl;
    }
    if (nodeCommon["logPath"].IsDefined()) {
         std::cout << "LogPath: " << nodeCommon["logPath"].as<std::string>() << std::endl;
    }
    
    // loadAbility(nodeCommon, ...)
    // loadPlugin(root["plugin"], ...)
}

读取ability:

void loadAbility(const YAML::Node &nodeCommon, std::vector<CommonAbility> &allAbility) {
    if (!nodeCommon["abilities"].IsDefined()) {
        std::cerr << "Node [engine/abilities] not found") << std::endl;
        return;
    }

    auto nodeAbility = nodeCommon["abilities"];
    allAbility.reserve(nodeAbility.size());
    for (auto eg : nodeAbility) {
        auto subAbility = eg["ability"];
        std::vector<std::string> vecAbility;
        vecAbility.reserve(subAbility.size());
        for (auto ab : subAbility) {
            vecAbility.emplace_back(ab.as<std::string>());
        }

        allAbility.emplace_back(CommonAbility{name:eg["name"].as<std::string>(), abilities:vecAbility});
    }
}

读取plugin:

void loadPlugins(const YAML::Node &plug, PluginInfo &config) {
    if (!plug["file"].IsDefined()) {
        std::cerr << "file of plugin [{}] not found!!", pl);
        return;
    }
    plInfo.file = plug["file"].as<std::string>();

    auto param = plug["param"];
    if (param.IsDefined()) {
        for (auto it = param.begin(); it != param.end(); ++it) {
            plInfo.param[it->first.as<std::string>()] = it->second.as<std::string>();
        }
    }
}

通过iterator枚举对象时,first为键,second为值。

 类似资料: