C ++를 사용하여 yaml 파일을 구문 분석하는 방법

1. yaml-cpp 설치

   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. yaml 구문

    YAML은 구성 파일을 작성하는 데 특별히 사용되는 언어로 JSON 및 xml 형식보다 매우 간결하고 강력하며 훨씬 편리합니다.

YAML 언어 (/ jæməl /로 발음)의 디자인 목표는 사람이 읽고 쓰기를 쉽게하는 것입니다. 본질적으로 범용 데이터 직렬화 형식입니다. 기본 문법 규칙은 다음과 같습니다.

  • 대소 문자 구분
  • 들여 쓰기를 사용하여 계층 적 관계 표시
  • 들여 쓰기시 Tab 키는 허용되지 않으며 공백 만 허용됩니다.
  • 같은 수준의 요소가 왼쪽에 정렬되는 한 들여 쓴 공백의 수는 중요하지 않습니다.
  • # 주석을 나타냅니다.이 문자부터 줄 끝까지 파서는 무시됩니다.

YAML에서 지원하는 세 가지 데이터 구조가 있습니다.

  • 개체 : 매핑 / 해시 / 사전이라고도하는 키-값 쌍 모음
  • 배열 : 순서대로 배열 된 일련의 값 (시퀀스 /리스트라고도 함)
  • 스칼라 : 나눌 수없는 단일 값

목적

    콜론 구조로 표시되는 오브젝트의 키-값 쌍 세트는 콜론 뒤의 공백에주의를 기울입니다.

    동물 : 애완 동물

    Yaml은 또한 모든 키-값 쌍을 인라인 개체로 작성하는 또 다른 방법을 허용합니다.

    해시 : {이름 : Steve, foo : bar}

정렬

접속사 라인의 시작 부분에있는 라인 그룹은 배열을 형성합니다. 접속사 라인에는 여전히 공백이 있습니다.

-고양이
-개
-금붕어

파이썬 목록과 동일

[ '고양이', '개', '금붕어']

데이터 구조의 하위 멤버는 배열이며 항목 아래에 공백을 들여 쓸 수 있습니다.

--
 고양이
 -개
 -금붕어

Python 목록과 동일

[[ '고양이', '개', '금붕어']]

배열은 인라인 표기법을 사용할 수도 있습니다.

동물 : [고양이, 개]

Python 사전과 동일

{동물 : [ '고양이', '개']}

복합 구조

객체와 배열을 결합하여 복합 구조를 형성 할 수 있습니다.

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

Python 사전과 동일

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

스칼라

스칼라는 가장 기본적이고 나눌 수없는 값입니다.

  • 부울 값
  • 정수
  • 부동 소수점
  • 없는
  • 시각
  • 데이트

값은 리터럴 형식으로 직접 표현됩니다.

번호 : 12.30

부울 값 truefalse표현.

isSet : true

null표시하는 데 사용합니다 ~.

부모 : ~

시간은 ISO8601 형식입니다.

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

날짜는 복합 iso8601 형식의 년, 월, 일로 표시됩니다.

날짜 : 1976-07-31

YAML에서는 두 개의 느낌표를 사용하여 데이터 유형을 강제 할 수 있습니다.

e : !! 123 페이지

f : !! str true

문자열은 가장 일반적이고 가장 복잡한 데이터 유형입니다. 문자열은 기본적으로 따옴표로 표현되지 않습니다.

str : 이것은 문자열의 한 줄입니다.

문자열에 공백이나 특수 문자가 포함 된 경우 따옴표로 묶어야합니다.

str : '내용 : 문자열'

작은 따옴표와 큰 따옴표를 모두 사용할 수 있으며 큰 따옴표는 특수 문자를 이스케이프하지 않습니다.

s1 : '내용 \ n 문자열'

s2 : "내용 \ n 문자열"

작은 따옴표 안에 작은 따옴표가 있으면 두 개의 작은 따옴표로 연속해서 이스케이프해야합니다.

str : '노동의 날'

문자열은 두 번째 줄부터 시작하여 여러 줄에 쓸 수 있으며 단일 공백 ​​들여 쓰기가 있어야합니다. 개행 문자는 공백으로 변환됩니다.

str : 이것은 단락입니다

  여러 줄

  끈

여러 줄 문자열의 경우 |를 사용하여 줄 바꿈을 유지하거나>를 사용하여 줄 바꿈을 축소 할 수 있습니다.

this: |
  Foo
  Bar
that: >
  Foo
  Bar

+텍스트 블록의 끝에 개행을 유지하는 -것을 의미하고 문자열 끝에서 개행을 삭제하는 것을 의미합니다.

s1: |
  Foo

s2: |+
  Foo

s3: |-
  Foo

HTML 태그를 문자열에 삽입 할 수 있습니다.

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

인용문

참조 용으로 앵커 &및 별칭을 *사용할 수 있습니다.

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

다음 코드와 동일합니다.

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

test:
  database: myapp_test
  adapter:  postgres
  host:     localhost

&앵커 포인트 ( defaults) 를 설정하는 데 사용됩니다. <<즉, 현재 데이터에 병합되어 *앵커 포인트를 참조하는 데 사용됩니다.

여기 또 다른 예가 있습니다.

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

3. yaml 분석

3.1 노드

Node는 yaml-cpp의 핵심 개념이자 가장 중요한 데이터 구조로 파싱 된 yaml 정보를 저장하는 데 사용됩니다. 노드에는 다음 유형이 있습니다.

널 노드

YAML 형식의 배열에 해당하는 벡터와 유사한 시퀀스 시퀀스

맵은 표준 라이브러리의 맵과 유사하며 YAML 형식의 객체에 해당합니다.

YAML 형식의 상수에 해당하는 스칼라 스칼라

Node를 생성하는 방법은 여러 가지가 있으며 loadFile ()이 가장 일반적인 방법입니다.

Node LoadFile(const std::string& filename)

filename은 yaml 파일의 경로입니다.

Node를 사용하면 모든 정보를 검색 할 수 있습니다. 이름과 같은.

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

as <string> ()은 구문 분석 된 내용을 문자열 유형으로 변환하는 것을 의미합니다.
다른 유형으로 변환 할 수도 있습니다.

템플릿 방식입니다.

3.2 yaml 파일 분석

예를 들어, 이러한 구성 파일 config.yaml

name: frank
sex: male
age: 18

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

yaml "," ":"의 내용 뒤에 공백이 와야합니다.
코드 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. 노드 추가, 수정 및 삭제

아래 코드를 직접 업로드하세요. 자세한 내용은 댓글을 참조하세요.

#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来删除它
}

실행 std::cout << node << endl;하면 다음과 같은 결과가 나타납니다.

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. yaml-cpp에서 반복

yaml-cpp에서 반복적 인 방식으로 Node의 콘텐츠에 액세스 할 수도 있습니다.

예를 들어 스킬 아래의 다양한 요소를 방문하십시오.

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 ()를 사용합니다.

6. 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에 각각 해당합니다.

7. yaml-cpp 쓰기 구성 파일 (yaml 파일 저장 및 읽기)

일상적인 개발에서 yaml 파일은 구성 파일로 자주 사용되며 구성 매개 변수를 읽는 것 외에도 매개 변수를 저장해야하는 경우가 많으며 yaml-cpp는 자연스럽게 해당 기능을 제공합니다.

노드는 파일 스트림 메소드를 사용하여 읽고 쓸 수 있습니다. 이전에 사용 된 적이 있습니다. 노드를 저장하려면 다음 메소드를 사용할 수 있습니다.

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

fout.close();

이렇게하면 위의 cout에 인쇄 된 내용이 config.yaml 파일로 출력됩니다.

노드를 읽으려면 다음을 수행 할 수 있습니다.

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. ROS에서 yaml 사용

ROS에서 사용하려면 CMakeLists.txt를 수정하고 yaml-cpp 라이브러리를 추가하고 roslib 패키지를 사용하여 yaml 파일의 경로를 설정해야합니다.

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

헤더 파일 #include <ros / package.h>를 소스 파일에 추가하십시오.

#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);  

참고:

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

추천

출처blog.csdn.net/sunlin972913894/article/details/103038082