概要
YAML (YAML Ain't Markup Language) は、人間にとって読み書きしやすいデータシリアライゼーション形式である。
設定ファイル、データ交換、データ保存等、様々な用途で使用されている。
YAMLの特徴を以下に示す。
- 可読性が高い
- インデントを使用して構造を表現するため、人間が読みやすい形式である。
- 豊富なデータ型
- 文字列、数値、ブール値、リスト、マップ (辞書) 等の基本的なデータ型をサポートしている。
- 柔軟性
- 複雑なデータ構造も表現できる。
- コメントのサポート
- #を使用して、コメントを記述することができる。
- 参照と別名
- データの再利用や循環参照が可能である。
YAMLの使用するメリットを以下に示す。
- 設定ファイルに適している
- 人間が読み書きしやすいため、アプリケーションの設定ファイルとしてよく使用されている。
- データ交換に適している
- JSONの代替として使用でき、より読みやすい形式でデータを表現できる。
- 柔軟性が高い
- 複雑なデータ構造も簡単に表現できる。
- 多くの言語でサポートされている
- 多くのプログラミング言語にYAMLパーサーが存在する。
YAMLは、可読性の高さと柔軟性から、多くの開発者に好まれている。
特に、設定ファイルやデータ交換の用途で広く使用されており、多くのモダンなアプリケーションやフレームワークでYAMLが採用されている。
C++では、YAMLの処理によく使用されるライブラリの1つにyaml-cppライブラリが存在する。
YAMLの基本的な構造
※注意
- インデントが重要
- スペースを使用してインデントを行い、一貫性を保つ必要がある。
- コロンの後にはスペースが必要
- "<キー>: <値>"のように、コロンの後にスペースを入れる必要がある。
- 文字列のクォート
- 特殊文字を含む場合や曖昧さを避けたい場合は、文字列をクォートで囲むことができる。
<syntaxhighlight lang="yaml">
# This is a comment
--- # Document start
# スカラー値 (文字列, 数値, ブール値)
name: John Doe
age: 30
is_student: false
# リスト (ハイフンを使用)
hobbies:
- reading
- traveling
- photography
# マッピング (キー: 値の形式)
address:
street: 123 Main St
city: Anytown
country: USA
# 複雑なデータ構造 (リストとマッピングの組み合わせ)
employees:
- name: Alice
position: Developer
skills:
- Python
- JavaScript
- name: Bob
position: Designer
skills:
- Photoshop
- Illustrator
# 複数行の文字列
description: >
This is a long description
that spans multiple lines.
The > symbol preserves newlines
but removes extra whitespace.
# 複数行の文字列 (> と | を使用)
code_sample: |
def hello_world():
print("Hello, World!")
# アンカー ("&") と エイリアス ("*") を使用したデータの再利用
defaults: &defaults
timeout: 30
retries: 3
production:
<<: *defaults
host: production.example.com
development: <<: *defaults host: dev.example.com timeout: 10 # デフォルト値のオーバーライド # Document end </syntaxhighlight>
yaml-cppライブラリ
yaml-cppライブラリとは
yaml-cppライブラリは、C++向けのYAMLパーサーおよびエミッタライブラリである。
yaml-cppライブラリを使用することにより、C++でYAMLファイルの読み込み、操作、書き込みが可能になる。
また、設定ファイルの処理、データのシリアライズ / デシリアライズ、構造化データの操作等に広く使用されている。
yaml-cppライブラリの特徴を以下に示す。
- C++ 11以降をサポート
- モダンなC++の機能を活用している。
- ヘッダオンリーライブラリ
- 使用する場合は、ヘッダファイルをインクルードするだけで済む。
- YAML 1.2仕様のサポート
- 最新のYAML仕様に準拠している。
- 例外ベースのエラー処理
- エラーが発生した場合は、適切な例外をスローする。
- STLとの統合
- STLコンテナとの相互運用性が高い。
- ストリーミングAPI
- 大きなファイルや継続的なデータストリームの処理に適している。
yaml-cppライブラリのライセンス
yaml-cppライブラリのライセンスは、MITライセンスに準拠している。
yaml-cppの主要なクラス
- YAML::Nodeクラス
- YAMLデータを表現する基本的なクラスである。
- スカラー、シーケンス、マップ等、あらゆる種類のYAMLノードを表現できる。
- YAML::Exceptionクラス
- yaml-cppライブラリが投げる例外の基本クラスである。
- パース時のエラーや型変換エラー等をキャッチできる。
- YAML::Emitterライブラリ
- YAMLデータを生成するためのクラスである。
- プログラム上において、YAMLを構築する時に使用する。
- YAML::Parserクラス
- 低レベルのパーシング操作を行うためのクラスである。
- 一般的には、
YAML::LoadクラスやYAML::LoadFileクラスを使用するため、直接使用することは少ない。
※注意
- 型安全性
YAML::Nodeクラスは、テンプレートベースの.as<T>メソッドを提供するが、型が一致しない場合は例外がスローされる。- そのため、適切な型チェックを行うことが重要である。
- メモリ管理
YAML::Nodeクラスのオブジェクトは参照カウント方式で管理されているため、通常はメモリリークを心配する必要は無い。
- パフォーマンス
- 大規模なファイルを扱う場合、
YAML::LoadFileクラスはファイル全体をメモリに読み込むため、メモリ使用量が増大する可能性がある。 - この場合、ストリーミングAPIの使用を検討すること。
- 大規模なファイルを扱う場合、
- エラー処理
- yaml-cppライブラリは例外を使用してエラーを報告する。
- そのため、適切な
try-catchブロックを使用して、エラーをハンドリングすることが重要である。
yaml-cppライブラリのインストール
パッケージ管理システムからインストール
# RHEL sudo dnf install yaml-cpp yaml-cpp-devel # SUSE sudo zypper install yaml-cpp yaml-cpp-devel
ソースコードからインストール
yaml-cppライブラリのGithubにアクセスして、ソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。
tar xf yaml-cpp-<バージョン>.tar.gz cd yaml-cpp-<バージョン>
yaml-cppライブラリをビルドおよびインストールする。
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=<yaml-cppライブラリのインストールディレクトリ> \
-DYAML_BUILD_SHARED_LIBS=ON \
..
make -j $(nproc)
make install
ライブラリの指定
Qtプロジェクトファイルを使用する場合
<syntaxhighlight lang="make"> # Qtプロジェクトファイル # pkg-configを使用してyaml-cppライブラリを設定 CONFIG += link_pkgconfig PKGCONFIG += yaml-cpp # pkg-configを使用しない場合 LIBS += -lyaml-cpp </syntaxhighlight>
CMakeLists.txtファイルを使用する場合
<syntaxhighlight lang="cmake">
# CMakeLists.txtファイル
# ...略
find_package(PkgConfig REQUIRED)
# yaml-cppライブラリの指定
pkg_check_modules(YAML_CPP REQUIRED yaml-cpp)
# インクルードディレクトリの指定
target_include_directories(${PROJECT_NAME} PRIVATE
# ...略
${YAML_CPP_INCLUDE_DIRS}
)
# ...略
# ライブラリのリンク
target_link_libraries(${PROJECT_NAME} PRIVATE
# ...略
${YAML_CPP_LIBRARIES}
)
# コンパイルオプションの設定
target_compile_options(${PROJECT_NAME} PRIVATE
# ...略
${YAML_CPP_CFLAGS_OTHER}
)
</syntaxhighlight>
YAMLファイルの読み込み
以下の例では、yaml-cppライブラリの主な機能を使用している。
- YAMLファイルの読み込み (
YAML::LoadFileクラスの使用) - スカラー値の取得 (
.as<T>メソッドの使用) - リストの処理 (範囲ベースのforループ)
- ネストされたマップの処理
- 新しいYAMLノードの作成と操作
- YAMLデータの出力 (ストリーム演算子
<<の使用) - YAMLデータのファイルへの書き込み
<syntaxhighlight lang="c++">
// main.cppファイル
#include <iostream>
#include <yaml-cpp/yaml.h>
int main()
{
try {
// YAMLファイルの読み込み
YAML::Node config = YAML::LoadFile("config.yaml");
// スカラー値の取得
std::string name = config["name"].as<std::string>();
int age = config["age"].as<int>();
std::cout << "Name: " << name << ", Age: " << age << std::endl;
// リストの処理
std::cout << "Hobbies:" << std::endl;
for (const auto &hobby : config["hobbies"]) {
std::cout << "- " << hobby.as<std::string>() << std::endl;
}
// ネストされたマップの処理
if (config["address"]) {
std::cout << "Address:" << std::endl;
std::cout << " Street: " << config["address"]["street"].as<std::string>() << std::endl;
std::cout << " City: " << config["address"]["city"].as<std::string>() << std::endl;
}
// 新しいYAMLノードの作成
YAML::Node newNode;
newNode["key"] = "value";
newNode["list"].push_back(1);
newNode["list"].push_back(2);
newNode["list"].push_back(3);
// YAMLの出力
std::cout << "\nNew YAML:\n" << newNode << std::endl;
// TAMLファイルへの書き込み
std::ofstream fout("output.yaml");
fout << newNode;
fout.close();
}
catch (const YAML::Exception& e) {
std::cerr << "YAML error: " << e.what() << std::endl;
return -1;
}
return 0;
}
</syntaxhighlight>
yaml-cppライブラリの詳細な使用方法は、公式ドキュメントを参照すること。
YAMLを出力するモデルは、std::ostreamマニピュレータである。
YAML::Emitterクラスのオブジェクトは出力ストリームとして動作して、その出力はc_strメソッドで取得できる。
YAML::Emitterクラスの詳細な使用方法は、公式ドキュメントを参照すること。