Cómo usar C ++ para analizar archivos yaml

1. Instale yaml-cpp

   Instalar la biblioteca yaml-cpp

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

2. sintaxis de yaml

    YAML es un lenguaje especialmente usado para escribir archivos de configuración, es muy conciso y poderoso, mucho más conveniente que los formatos JSON y xml.

El objetivo del diseño del lenguaje YAML (pronunciado / ˈjæməl /) es facilitar la lectura y la escritura humanas. Es esencialmente un formato de serialización de datos universal. Sus reglas gramaticales básicas son las siguientes.

  • Distingue mayúsculas y minúsculas
  • Use sangría para indicar relaciones jerárquicas
  • La tecla Tab no está permitida al sangrar, solo se permiten espacios.
  • El número de espacios con sangría no es importante, siempre que los elementos del mismo nivel estén alineados a la izquierda
  • # Indica un comentario. Desde este carácter hasta el final de la línea, será ignorado por el analizador.

Hay tres estructuras de datos compatibles con YAML.

  • Objeto: una colección de pares clave-valor, también conocida como mapeo / hash / diccionario
  • Matriz: un conjunto de valores dispuestos en orden, también conocido como secuencia / lista
  • Escalares: un valor único e indivisible

Objeto

    Un conjunto de pares clave-valor del objeto, representado por una estructura de dos puntos, presta atención a un espacio después de los dos puntos.

    animal: mascotas

    Yaml también permite otra forma de escribir todos los pares clave-valor como un objeto en línea.

    hash: {nombre: Steve, foo: bar}

Formación

Un grupo de líneas al comienzo de la línea de conjunción forman una matriz. Tenga en cuenta que todavía hay un espacio en la línea de conjunción.

- Gato
- Perro
- Pez dorado

Equivalente a la lista de Python

['Gato', 'Perro', 'Pez dorado']

El miembro secundario de la estructura de datos es una matriz, puede sangrar un espacio debajo del elemento.

-
 - Gato
 - Perro
 - Pez dorado

Equivalente a la lista de Python

[['Gato', 'Perro', 'Pez dorado']]

Las matrices también pueden usar notación en línea.

animal: [gato, perro]

Equivalente al diccionario Python

{animal: ['Gato', 'Perro']}

Estructura compuesta

Los objetos y las matrices se pueden combinar para formar una estructura compuesta.

languages:
 - Ruby
 - Perl
 - Python 
websites:
 YAML: yaml.org 
 Ruby: ruby-lang.org 
 Python: python.org 
 Perl: use.perl.org 

Equivalente al diccionario Python

{ languages: [ 'Ruby', 'Perl', 'Python' ],
  websites: 
   { YAML: 'yaml.org',
     Ruby: 'ruby-lang.org',
     Python: 'python.org',
     Perl: 'use.perl.org' } }

Escalar

Escalar es el valor más básico e indivisible

  • Cuerda
  • Valor booleano
  • Entero
  • Punto flotante
  • Nulo
  • hora
  • fecha

El valor se expresa directamente en forma literal.

número: 12.30

Valor booleano truey falserepresentar.

isSet: verdadero

nullÚselo para ~indicar.

padre: ~

La hora está en formato ISO8601.

iso8601: 2001-12-14t21: 59: 43.10-05: 00

La fecha está representada por el año, mes y día en el formato compuesto iso8601.

fecha: 1976-07-31

YAML permite el uso de dos signos de exclamación para forzar el tipo de datos.

e: !! p 123

f: !! str cierto

Cuerda

Las cadenas son el tipo de datos más común y complejo. Las cadenas no se expresan entre comillas de forma predeterminada.

str: esta es una línea de cadena

Si la cadena contiene espacios o caracteres especiales, estos deben ir entre comillas.

str: 'Contenido: Cadena'

Se pueden usar comillas simples y dobles, y las comillas dobles no escaparán a los caracteres especiales.

s1: 'contenido \ ncadena'

s2: "Content \ nString"

Si hay una comilla simple en la comilla simple, se debe omitir con dos comillas simples consecutivamente.

str: 'día del trabajo'

La cadena se puede escribir en varias líneas, comenzando desde la segunda línea, debe haber una sangría de un solo espacio. Los caracteres de nueva línea se convertirán en espacios.

str: este es un párrafo

  Multilínea

  Cuerda

Para cadenas de varias líneas, puede utilizar | para conservar las nuevas líneas o utilizar> para contraer las nuevas líneas.

this: |
  Foo
  Bar
that: >
  Foo
  Bar

+Significa mantener la nueva línea al final del bloque de texto, -significa eliminar la nueva línea al final de la cadena.

s1: |
  Foo

s2: |+
  Foo

s3: |-
  Foo

Se pueden insertar etiquetas HTML en la cadena.

message: |
  <p style="color: red">
    段落
  </p>

Citar

Los anclajes &y los alias *se pueden utilizar como referencia.

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

Es equivalente al siguiente código.

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

test:
  database: myapp_test
  adapter:  postgres
  host:     localhost

&Se utiliza para establecer un punto de ancla ( defaults), lo que <<significa que se fusiona con los datos actuales y se *utiliza para hacer referencia al punto de ancla.

He aquí otro ejemplo.

- &showell Steve 
- Clark 
- Brian 
- Oren 
- *showell 

3. Análisis de yaml

3.1 Nodo

Nodo es el concepto central en yaml-cpp y la estructura de datos más importante.Se usa para almacenar la información de yaml analizada. El nodo tiene los siguientes tipos:

Nodo nulo

Secuencia de secuencia, similar a un vector, correspondiente a la matriz en formato YAML

Map es similar a Map en la biblioteca estándar, correspondiente a objetos en formato YAML

Escalar escalar, correspondiente a constantes en formato YAML

Hay muchas formas de generar Node, loadFile () es la más común.

Node LoadFile(const std::string& filename)

nombre de archivo es la ruta del archivo yaml.

Con Node, se puede recuperar toda la información. Como nombre.

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

como <cadena> () significa convertir el contenido analizado en un tipo de cadena.
También puede convertir a otros tipos.

Es un método de plantilla.

3.2 Análisis del archivo yaml

Por ejemplo, un archivo de configuración config.yaml

name: frank
sex: male
age: 18

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

Tenga en cuenta que el contenido en yaml "," ":" debe ir seguido de un espacio.
Analice el código yaml_test.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;

    //可以用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. Agregar, modificar y eliminar un nodo

Cargue directamente el código a continuación, consulte los comentarios para obtener más detalles.

#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的一个子项
    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来删除它
}

Si lo ejecuta, std::cout << node << endl;debería obtener los siguientes resultados:

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

5. Iteración en yaml-cpp

En yaml-cpp, también puede acceder al contenido en Node de manera iterativa.

Por ejemplo, visite los diversos elementos en habilidades.

for(YAML::const_iterator it= config["skills"].begin(); it != config["skills"].end();++it)
{
    cout << it->first.as<string>() << ":" << it->second.as<int>() << endl;
}

Use begin () para obtener el iterador y end () para determinar si el iterador ha terminado.

6. Tipo de NodeType

yaml admite los tipos Scalar, List y Map, y yaml-cpp define los posibles tipos de Node a través de NodeType.

namespace YAML {
struct NodeType {
  enum value { Undefined, Null, Scalar, Sequence, Map };
};
}

Corresponde a indefinido, vacío, escalar, secuencia, diccionario.

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;

El código anterior es para determinar el NodeType.

Los resultados son los siguientes:

 Type: 3
 Type: 2
 Type: 4

Corresponden a Sequence, Scalar y Map respectivamente.

7. archivo de configuración de escritura yaml-cpp (guardar y leer archivo yaml)

En el desarrollo diario, los archivos yaml se utilizan a menudo como archivos de configuración. Además de leer los parámetros de configuración, a menudo necesitamos guardar los parámetros, y yaml-cpp naturalmente proporciona las funciones correspondientes.

El nodo puede usar el método de flujo de archivos para leer y escribir. Se ha usado antes. Para guardar un nodo, puede usar el siguiente método:

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

fout.close();

De esta manera, el contenido impreso en cout arriba se enviará al archivo config.yaml.

Para leer un nodo, puede hacer:

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;//也可以用迭代器访问

8. Usa yaml en ROS

Para usar en ROS, debe modificar CMakeLists.txt, agregar la biblioteca yaml-cpp y usar un paquete roslib para establecer la ruta del archivo yaml.

link_libraries(yaml-cpp)
find_package(catkin REQUIRED COMPONENTS
  roslib
)

Agregue el archivo de encabezado #include <ros / package.h> al archivo de origen

#include <ros/package.h>
......
std::string dir_package, dir_package_file;
dir_package = ros::package::getPath("xxx");
dir_package_file = dir_package + "/config/test.yaml";
loadYamlFile(dir_package_file);  

referencia:

https://en.wikipedia.org/wiki/YAML
https://yaml.org/spec/1.2/spec.html

https://www.ruanyifeng.com/blog/2016/07/yaml.html

https://www.cnblogs.com/huodaozhe/p/12026327.html

https://blog.csdn.net/briblue/article/details/89515470

Supongo que te gusta

Origin blog.csdn.net/sunlin972913894/article/details/103038082
Recomendado
Clasificación