Qtの基礎 - YAML
概要
YAML (YAML Ain't Markup Language) は、人間にとって読み書きしやすいデータシリアライゼーション形式である。
設定ファイル、データ交換、データ保存等、様々な用途で使用されている。
YAMLの特徴を以下に示す。
- 可読性が高い
- インデントを使用して構造を表現するため、人間が読みやすい形式である。
- 豊富なデータ型
- 文字列、数値、ブール値、リスト、マップ (辞書) 等の基本的なデータ型をサポートしている。
- 柔軟性
- 複雑なデータ構造も表現できる。
- コメントのサポート
- #を使用して、コメントを記述することができる。
- 参照と別名
- データの再利用や循環参照が可能である。
YAMLの使用するメリットを以下に示す。
- 設定ファイルに適している
- 人間が読み書きしやすいため、アプリケーションの設定ファイルとしてよく使用されている。
- データ交換に適している
- JSONの代替として使用でき、より読みやすい形式でデータを表現できる。
- 柔軟性が高い
- 複雑なデータ構造も簡単に表現できる。
- 多くの言語でサポートされている
- 多くのプログラミング言語にYAMLパーサーが存在する。
YAMLは、可読性の高さと柔軟性から、多くの開発者に好まれている。
特に、設定ファイルやデータ交換の用途で広く使用されており、多くのモダンなアプリケーションやフレームワークでYAMLが採用されている。
Qtには、YAML処理のための直接的なサポートが無いため、サードパーティのライブラリを使用する必要がある。
YAMLの処理によく使用されるライブラリの1つにyaml-cppライブラリが存在する。
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
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プロジェクトファイルを使用する場合
# Qtプロジェクトファイル
# pkg-configを使用してyaml-cppライブラリを設定
CONFIG += link_pkgconfig
PKGCONFIG += yaml-cpp
# pkg-configを使用しない場合
LIBS += -lyaml-cpp
CMakeLists.txtファイルを使用する場合
# 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}
)
YAMLファイルの読み込み
以下の例では、指定したYAMLファイルを読み込み、その内容を表示している。
// main.cppファイル
#include <QCoreApplication>
#include <yaml-cpp/yaml.h>
#include <iostream>
#include <QDebug>
// YAMLファイルを読み込む関数
YAML::Node readYamlFile(const QString& filePath)
{
try {
return YAML::LoadFile(filePath.toStdString());
}
catch (const YAML::Exception& e) {
qDebug() << "YAMLファイルの読み込みに失敗: " << e.what();
return YAML::Node();
}
}
// YAMLノードの内容を表示する関数 (再帰的)
void printNode(const YAML::Node& node, int indent = 0)
{
QString indentStr = QString(" ").repeated(indent);
switch (node.Type()) {
case YAML::NodeType::Scalar:
qDebug().noquote() << indentStr << node.as<std::string>().c_str();
break;
case YAML::NodeType::Sequence:
for (const auto& item : node) {
printNode(item, indent + 2);
}
break;
case YAML::NodeType::Map:
for (const auto& it : node) {
qDebug().noquote() << indentStr << it.first.as<std::string>().c_str() << ":";
printNode(it.second, indent + 2);
}
break;
default:
qDebug().noquote() << indentStr << "不明なノードタイプ";
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// YAMLファイルを読み込む
QString inputFile = "sample.yaml";
YAML::Node config = readYamlFile(inputFile);
if (config.IsNull()) {
qDebug() << "YAMLファイルの読み込みに失敗";
return -1;
}
qDebug() << "YAMLファイルの内容";
printNode(config);
return a.exec();
}
YAMLファイルの書き込み
以下の例では、読み込んだYAMLファイルのデータを変更して、変更したデータを新しいYAMLファイルに書き込んでいる。
// main.cppファイル
#include <QCoreApplication>
#include <yaml-cpp/yaml.h>
#include <fstream>
#include <QDebug>
// YAMLファイルを書き込む関数
bool writeYamlFile(const QString& filePath, const YAML::Node& node)
{
try {
std::ofstream fout(filePath.toStdString());
fout << node;
return true;
}
catch (const YAML::Exception& e) {
qDebug() << "YAMLファイルの書き込みに失敗: " << e.what();
return false;
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 新しいYAMLノードを作成
YAML::Node config;
config["name"] = "John Doe";
config["age"] = 30;
config["city"] = "New York";
YAML::Node hobbies;
hobbies.push_back("reading");
hobbies.push_back("traveling");
hobbies.push_back("photography");
config["hobbies"] = hobbies;
config["job"]["title"] = "Software Engineer";
config["job"]["company"] = "Tech Corp";
// データをYAMLファイルに書き込む
QString outputFile = "sample2.yaml";
if (writeYamlFile(outputFile, config)) {
qDebug() << "YAMLファイルの書き込みに成功: " << outputFile;
}
else {
qDebug() << "YAMLファイルの書き込みに失敗";
return -1;
}
return a.exec();
}
yaml-cppライブラリの詳細な使用方法は、公式ドキュメントを参照すること。
YAMLを出力するモデルは、std::ostream
マニピュレータである。
YAML::Emitter
クラスのオブジェクトは出力ストリームとして動作して、その出力はc_str
メソッドで取得できる。
YAML::Emitter
クラスの詳細な使用方法は、公式ドキュメントを参照すること。