Qtの基礎 - YAML

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

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クラスの詳細な使用方法は、公式ドキュメントを参照すること。