【Qt入門】QSettings::setArrayIndex() を使ったデータ保存の実践例

2025-05-27

QSettings::setArrayIndex() とは

QSettings クラスは、アプリケーションの設定を永続的に保存および読み込むための Qt のクラスです。通常、設定はキーと値のペアとして保存されますが、リストや配列のようなデータを保存したい場合もあります。

QSettings::setArrayIndex(int i) 関数は、QSettings で配列状のデータを扱う際に、現在操作する配列の要素のインデックスを設定するために使用されます。

配列の保存と読み込みの基本的な流れ

QSettings で配列を扱うには、beginWriteArray() または beginReadArray()endArray() のペアを使用します。setArrayIndex() は、この begin/endArray ブロック内で各配列要素にアクセスするために呼び出されます。

配列の書き込み (beginWriteArray())

配列にデータを書き込む際は、まず beginWriteArray() を呼び出します。これにより、指定されたプレフィックスの下に新しい配列が開始されます。

#include <QSettings>
#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QSettings settings("MyCompany", "MyApp"); // 会社名とアプリケーション名を設定

    settings.beginWriteArray("scores"); // "scores" という名前の配列を開始
    for (int i = 0; i < 5; ++i) {
        settings.setArrayIndex(i); // 現在の配列インデックスを設定
        settings.setValue("value", i * 10); // そのインデックスに値を保存
    }
    settings.endArray(); // 配列の書き込みを終了

    qDebug() << "Settings written.";

    return 0;
}

このコードでは、"scores" という名前の配列を作成し、0から4までのインデックスで value というキーに値を保存しています。例えば、設定ファイルには以下のように保存されることがあります(INI形式の場合の例):

[scores]
size=5
0\value=0
1\value=10
2\value=20
3\value=30
4\value=40

注釈: size エントリは beginWriteArray() で自動的に設定されます。

配列の読み込み (beginReadArray())

配列からデータを読み込む際は、beginReadArray() を呼び出します。これにより、配列の要素数を取得し、ループで各要素にアクセスできるようになります。

#include <QSettings>
#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QSettings settings("MyCompany", "MyApp"); // 会社名とアプリケーション名を設定

    int size = settings.beginReadArray("scores"); // "scores" 配列の読み込みを開始し、要素数を取得
    qDebug() << "Array size:" << size;

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i); // 現在の配列インデックスを設定
        int value = settings.value("value").toInt(); // そのインデックスから値を読み込む
        qDebug() << "Score at index" << i << ":" << value;
    }
    settings.endArray(); // 配列の読み込みを終了

    return 0;
}

このコードを実行すると、保存された配列の要素が読み込まれ、それぞれの値が表示されます。

setArrayIndex() の役割

setArrayIndex() が行うのは、QSettings オブジェクトの内部状態を変更し、その後の setValue()value() の呼び出しが、指定された配列インデックスに対応するキーに作用するようにすることです。

例えば、settings.setArrayIndex(i); を呼び出した後、settings.setValue("key", val); を呼び出すと、内部的には "scores/i/key" のような形式で値が保存されます(実際の内部的なキー名はフォーマットによって異なる場合がありますが、概念的にはインデックスがパスの一部になります)。

  • パフォーマンス
    多数の要素を持つ配列を扱う場合、QSettings のパフォーマンスは、ファイルの読み書きが頻繁に発生するため、ボトルネックになる可能性があります。
  • ネストされた配列
    QSettings は、配列のネスト(配列の中にさらに配列を持つ構造)を直接サポートしていません。より複雑なデータ構造を保存したい場合は、JSON(QJsonDocument)やカスタムのシリアル化メカニズムを検討する方が適切です。
  • インデックスの連続性
    QSettings は配列のインデックスの連続性を強制しません。しかし、通常は0から始まり連続したインデックスを使用することが推奨されます。
  • beginArray/endArray ペア
    setArrayIndex() は、必ず beginReadArray() または beginWriteArray() の後、かつ endArray() の前に呼び出す必要があります。これらのペアがないと、期待通りに動作しません。


QSettings::setArrayIndex() は、beginWriteArray() / endArray() または beginReadArray() / endArray() と組み合わせて使用されるため、これらの関数との連携がうまくいかない場合に問題が発生しやすいです。

beginArray/endArray の呼び忘れ、または誤った使用順序

エラーの症状

  • デバッグ出力を見ても、設定のパスが期待と異なる。
  • プログラムがクラッシュしたり、奇妙な動作をしたりする。
  • 予期せぬキー名で設定が保存される(例: 配列ではない通常のキーとして保存される)。
  • 設定が正しく保存されない、または読み込まれない。

原因
setArrayIndex() は、配列コンテキスト内でのみ意味を持ちます。beginWriteArray()beginReadArray() を呼び出すことで配列コンテキストに入り、endArray() でそのコンテキストを終了します。このペアの呼び出しを忘れたり、順序が間違っていたりすると、setArrayIndex() が意図した通りに機能しません。

トラブルシューティング

  • ネストの問題
    誤って配列の処理中に別の beginGroup()beginWriteArray() を呼び出してしまい、コンテキストが入れ子になって混乱している場合があります。シンプルな構造から確認してみてください。
  • ペアになっているか
    begin...Array()endArray() は必ずペアで使用する必要があります。どちらか一方だけが呼び出されている場合、問題が発生します。
  • beginWriteArray() / endArray() の確認
    • 書き込み時: settings.beginWriteArray("arrayName"); の後にループ処理があり、そのループ内で settings.setArrayIndex(i); を呼び、ループの後に settings.endArray(); を呼び出していることを確認します。
    • 読み込み時: settings.beginReadArray("arrayName"); の後にループ処理があり、そのループ内で settings.setArrayIndex(i); を呼び、ループの後に settings.endArray(); を呼び出していることを確認します。

配列のサイズとインデックスの不一致

エラーの症状

  • 書き込み時に想定よりも少ない要素しか保存されない。
  • 読み込み時に配列の要素が途中で切れる、または存在しないインデックスにアクセスしようとしてエラーが発生する。

原因

  • 読み込み時
    beginReadArray() が返す配列のサイズと、ループの回数が一致していない。読み込みループで配列のサイズを超えたインデックスにアクセスしようとすると、無効な値を取得したり、クラッシュしたりする可能性があります。
  • 書き込み時
    beginWriteArray() に渡す配列のサイズ(要素数)と、実際に setArrayIndex() で設定するインデックスの範囲が一致していない。

トラブルシューティング

  • 読み込み時のループ条件
    • int size = settings.beginReadArray("arrayName"); で取得した size をループの終端条件に必ず使用します。
    • 例: for (int i = 0; i < size; ++i) { ... }
  • beginWriteArray(const QString &prefix, int size = -1) の size 引数
    • 書き込み時に要素数が確定している場合、beginWriteArray("scores", actualSize); のように size 引数を明示的に指定すると、Qtが内部的に配列のサイズを記録します。これにより、読み込み時に正確なサイズ (beginReadArray() の戻り値) を取得できます。
    • setArrayIndex() で設定するインデックスが、この size の範囲内であることを確認します(通常は 0 から size - 1)。

キー名の重複や競合

エラーの症状

  • 設定が上書きされてしまう。
  • 期待していた値が読み込まれず、別の値が表示される。

原因
setArrayIndex() を使用しているにもかかわらず、配列の各要素内で使用するキー名が重複している場合、あるいは配列外のキー名と重複している場合に、設定が意図せず上書きされることがあります。

トラブルシューティング

  • 配列名との競合
    beginWriteArray("myArray") とした後に、誤って settings.setValue("myArray", ...) のような配列名と同じキーに値を保存すると、配列自体が上書きされてしまう可能性があります。これは避けるべきです。
  • 配列内のキー名
    setArrayIndex() を呼び出した後、そのインデックス内で使用するキー名(例: setValue("value", ...)"value")が、そのインデックス内でのみユニークであることを確認します。通常、同じキー名を複数の要素で使い回しても問題ありませんが、もし要素ごとに異なる種類のデータを保存したい場合は、キー名も区別する必要があります。

ファイルパスやパーミッションの問題

エラーの症状

  • QSettings::status()AccessErrorFormatError を返す。
  • 設定が保存されない、または読み込まれない。

原因
QSettings が設定ファイルを書き込む、または読み込むための適切なパーミッションがない場合や、ファイルが破損している場合に発生します。これは setArrayIndex() に直接関連するエラーではありませんが、配列の保存や読み込みが機能しない根本原因になり得ます。

トラブルシューティング

  • 設定ファイルの破損
    設定ファイル(例: .ini ファイルやレジストリエントリ)を手動で確認し、フォーマットが正しいか、破損していないかを確認します。INIファイルの場合、テキストエディタで開いて内容を確認できます。
  • パーミッションの確認
    アプリケーションを実行しているユーザーが、設定ファイルが存在するディレクトリに対する読み書き権限を持っているかを確認します。
  • ファイルパスの確認
    QSettings のファイルパス (settings.fileName()) を確認し、そのパスにファイルが存在するか、アクセス可能かを確認します。

デバッグとログ出力の活用

  • QSettings::allKeys()
    デバッグ目的で、settings.allKeys() を呼び出して現在 QSettings オブジェクトが認識しているすべてのキーを出力してみると、意図しないキーが存在しないか、期待するキーが正しいパスで存在しているかを確認できます。
  • QSettings::status() の確認
    QSettings オブジェクトの status() メソッドを呼び出すことで、最後に発生したエラーの種類(QSettings::NoError, QSettings::AccessError, QSettings::FormatError)を確認できます。これにより、問題の切り分けがしやすくなります。
  • QDebug の活用
    qDebug() を使って、beginReadArray() が返すサイズ、setArrayIndex() で設定しているインデックス、そして value() で読み込んでいる値などを出力し、期待通りのデータが処理されているかを確認します。


QSettings::setArrayIndex() は、主にリストや配列のような複数の設定項目を保存・読み込みたい場合に使用します。ここでは、一般的な保存と読み込みの例を挙げ、その使い方を詳細に説明します。

例1: 文字列リストの保存と読み込み

この例では、ユーザーのお気に入りの色をリストとして保存し、後で読み込む方法を示します。

ヘッダーファイル (main.cpp と同じファイルに記述することも可能ですが、ここでは分割を想定)

// main.cpp
#include <QCoreApplication>
#include <QSettings>
#include <QStringList>
#include <QDebug>

void saveFavoriteColors(const QStringList& colors);
QStringList loadFavoriteColors();

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // アプリケーション情報の設定 (QSettingsが適切に動作するために重要)
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("ColorPickerApp");

    // 1. 色のリストを保存する
    QStringList myColors;
    myColors << "Red" << "Green" << "Blue" << "Yellow";
    saveFavoriteColors(myColors);
    qDebug() << "Saved colors:" << myColors;

    // 2. 色のリストを読み込む
    QStringList loadedColors = loadFavoriteColors();
    qDebug() << "Loaded colors:" << loadedColors;

    return 0;
}

// 色のリストをQSettingsに保存する関数
void saveFavoriteColors(const QStringList& colors) {
    QSettings settings; // デフォルトではQCoreApplication::setOrganizationName/setApplicationNameを使用

    settings.beginWriteArray("FavoriteColors", colors.size()); // "FavoriteColors" という名前で配列を開始
                                                              // colors.size() を指定することで、読み込み時にサイズがわかる
    for (int i = 0; i < colors.size(); ++i) {
        settings.setArrayIndex(i); // 現在操作する配列のインデックスを設定
        settings.setValue("color", colors.at(i)); // "color" というキーで現在の色を保存
    }
    settings.endArray(); // 配列の書き込みを終了
}

// QSettingsから色のリストを読み込む関数
QStringList loadFavoriteColors() {
    QSettings settings;
    QStringList loadedColors;

    int size = settings.beginReadArray("FavoriteColors"); // "FavoriteColors" 配列の読み込みを開始し、要素数を取得
    qDebug() << "Found" << size << "favorite colors.";

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i); // 現在操作する配列のインデックスを設定
        QString color = settings.value("color").toString(); // "color" というキーで色を読み込む
        loadedColors.append(color);
    }
    settings.endArray(); // 配列の読み込みを終了

    return loadedColors;
}

解説

  1. QCoreApplication::setOrganizationName()setApplicationName(): QSettings が設定ファイルをどこに保存するかを決定するために、これらの情報が必要です。Windowsではレジストリ、macOSではplistファイル、LinuxではXDG Base Directory Specificationに準拠したファイルが使用されます。
  2. saveFavoriteColors() 関数:
    • settings.beginWriteArray("FavoriteColors", colors.size());: FavoriteColors という名前の配列の書き込みを開始します。colors.size() を渡すことで、この配列がいくつの要素を持つかを QSettings に伝えます。これにより、後で読み込む際にループの回数を正確に知ることができます。
    • settings.setArrayIndex(i);: ループ内で、現在処理している配列の要素のインデックスを i に設定します。これにより、その後の setValue() はこのインデックスのパスに関連付けられます。
    • settings.setValue("color", colors.at(i));: 現在のインデックスの下に、"color" というキーで色を保存します。例えば、インデックス0では FavoriteColors/0/color のような内部パスが形成されます(実際のファイル形式は異なる場合があります)。
    • settings.endArray();: 配列の書き込みを終了します。
  3. loadFavoriteColors() 関数:
    • int size = settings.beginReadArray("FavoriteColors");: FavoriteColors 配列の読み込みを開始し、saveFavoriteColors で指定した配列の要素数を返します。
    • settings.setArrayIndex(i);: ループ内で、現在読み込んでいる配列のインデックスを設定します。
    • QString color = settings.value("color").toString();: 現在のインデックスの下の "color" キーから値を読み込みます。
    • settings.endArray();: 配列の読み込みを終了します。

例2: 構造体(オブジェクト)のリストの保存と読み込み

より複雑なデータ、例えばユーザー設定のオブジェクトをリストとして保存したい場合を考えます。ここでは、簡単な User 構造体を定義します。

#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QList>

// ユーザー情報を保持する構造体
struct User {
    QString name;
    int age;
    bool isActive;

    // デバッグ出力用
    friend QDebug operator<<(QDebug debug, const User& user) {
        QDebugStateSaver saver(debug);
        debug.nospace() << "User(Name: " << user.name << ", Age: " << user.age
                        << ", Active: " << user.isActive << ")";
        return debug;
    }
};

void saveUsers(const QList<User>& users);
QList<User> loadUsers();

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("UserManagementApp");

    // 1. ユーザーリストを作成
    QList<User> userList;
    userList << User{"Alice", 30, true}
             << User{"Bob", 25, false}
             << User{"Charlie", 35, true};

    // 2. ユーザーリストを保存
    saveUsers(userList);
    qDebug() << "Saved users:";
    for (const User& user : userList) {
        qDebug() << user;
    }

    // 3. ユーザーリストを読み込む
    QList<User> loadedUsers = loadUsers();
    qDebug() << "\nLoaded users:";
    for (const User& user : loadedUsers) {
        qDebug() << user;
    }

    return 0;
}

// ユーザーリストをQSettingsに保存する関数
void saveUsers(const QList<User>& users) {
    QSettings settings;

    settings.beginWriteArray("Users", users.size());
    for (int i = 0; i < users.size(); ++i) {
        settings.setArrayIndex(i); // インデックスを設定

        // 各ユーザーのプロパティを保存
        settings.setValue("name", users.at(i).name);
        settings.setValue("age", users.at(i).age);
        settings.setValue("isActive", users.at(i).isActive);
    }
    settings.endArray();
}

// QSettingsからユーザーリストを読み込む関数
QList<User> loadUsers() {
    QSettings settings;
    QList<User> loadedUsers;

    int size = settings.beginReadArray("Users");
    qDebug() << "Found" << size << "users.";

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i); // インデックスを設定

        User user;
        user.name = settings.value("name").toString();
        user.age = settings.value("age").toInt();
        user.isActive = settings.value("isActive").toBool();
        loadedUsers.append(user);
    }
    settings.endArray();

    return loadedUsers;
}

解説

この例では、User 構造体の各メンバーを、対応する配列のインデックスの下の個別のキーとして保存しています。

  • settings.setValue("isActive", ...)
  • settings.setValue("age", ...)
  • settings.setValue("name", ...)

これにより、QSettings は内部的に Users/0/name, Users/0/age, Users/0/isActive, Users/1/name, ... のようなパスでデータを管理します。

  • エラーハンドリング: 実際のアプリケーションでは、QSettings::status() をチェックしてエラー(ファイルアクセス権の問題など)がないか確認することをお勧めします。
  • ファイル形式: 使用される設定ファイルの形式(INIファイル、XMLファイル、Windowsレジストリなど)は、プラットフォームや QSettings のコンストラクタに渡す引数によって異なります。
  • デフォルトのコンストラクタ: QSettings settings; のように引数なしで QSettings オブジェクトを作成した場合、Qt は上記で設定した組織名とアプリケーション名を使用して設定ファイルを自動的に管理します。
  • QCoreApplication::setOrganizationName()setApplicationName(): これらの関数は、QSettings が設定ファイルを保存する場所を決定するために非常に重要です。通常、アプリケーションの起動時に一度だけ呼び出します。


QSettings::setArrayIndex() の代替方法

QSettings を直接使用する(手動でインデックスを管理)

setArrayIndex() を使わずに、手動でキー名にインデックスを含めることで、同様の配列構造を実現できます。これは、配列の要素数が少ない場合や、柔軟なキー名が必要な場合に有効です。

メリット

  • より柔軟なキー命名が可能(例: item_id_123 のようにインデックス以外の情報も含む)。
  • beginArray() / endArray() の呼び出しが不要で、コードがシンプルになることがある。

デメリット

  • 要素の削除や挿入が複雑になる可能性がある。
  • 配列のサイズを別途保存・読み込みする必要がある。


#include <QCoreApplication>
#include <QSettings>
#include <QStringList>
#include <QDebug>

void saveColorsManual(const QStringList& colors) {
    QSettings settings;
    settings.setValue("ColorCount", colors.size()); // 要素数を保存

    for (int i = 0; i < colors.size(); ++i) {
        settings.setValue(QString("Color_%1").arg(i), colors.at(i)); // 手動でインデックスをキー名に含める
    }
}

QStringList loadColorsManual() {
    QSettings settings;
    QStringList loadedColors;

    int count = settings.value("ColorCount", 0).toInt(); // 要素数を読み込み
    for (int i = 0; i < count; ++i) {
        loadedColors.append(settings.value(QString("Color_%1").arg(i)).toString());
    }
    return loadedColors;
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("ManualSettingsApp");

    QStringList myColors;
    myColors << "Red" << "Green" << "Blue";
    saveColorsManual(myColors);
    qDebug() << "Saved (Manual):" << myColors;

    QStringList loadedColors = loadColorsManual();
    qDebug() << "Loaded (Manual):" << loadedColors;

    return 0;
}

JSON形式で保存・読み込み (QJsonDocument, QJsonObject, QJsonArray)

複雑なデータ構造(ネストされたオブジェクトや配列を含む)を保存する場合、JSON (JavaScript Object Notation) は非常に強力で読みやすい形式です。Qtは QJsonDocument, QJsonObject, QJsonArray クラスを提供しており、これらを組み合わせてデータをシリアル化できます。

メリット

  • QSettings よりも柔軟なデータ型を直接サポート。
  • 異なるアプリケーションやシステムとのデータ交換が容易。
  • 人間が読みやすく、デバッグしやすい。
  • 構造化されたデータを容易に表現できる。

デメリット

  • バイナリ形式よりもファイルサイズが大きくなる可能性がある。
  • QSettings のようにプラットフォームネイティブなストレージ(レジストリなど)を直接利用しないため、ファイルとして保存する必要がある。


#include <QCoreApplication>
#include <QSettings> // ファイルパスの取得などで使用
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QDebug>

struct User {
    QString name;
    int age;
    bool isActive;
};

// ユーザーリストをJSON形式で保存する関数
void saveUsersToJson(const QList<User>& users) {
    QJsonArray userArray;
    for (const User& user : users) {
        QJsonObject userObject;
        userObject["name"] = user.name;
        userObject["age"] = user.age;
        userObject["isActive"] = user.isActive;
        userArray.append(userObject);
    }

    QJsonDocument doc(userArray);
    QFile file("users.json"); // 設定ファイルパス
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        file.write(doc.toJson());
        file.close();
        qDebug() << "Users saved to users.json";
    } else {
        qWarning() << "Failed to open users.json for writing:" << file.errorString();
    }
}

// JSON形式からユーザーリストを読み込む関数
QList<User> loadUsersFromJson() {
    QList<User> loadedUsers;
    QFile file("users.json");
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QByteArray jsonData = file.readAll();
        file.close();

        QJsonDocument doc = QJsonDocument::fromJson(jsonData);
        if (doc.isArray()) {
            QJsonArray userArray = doc.array();
            for (const QJsonValue& value : userArray) {
                if (value.isObject()) {
                    QJsonObject userObject = value.toObject();
                    User user;
                    user.name = userObject["name"].toString();
                    user.age = userObject["age"].toInt();
                    user.isActive = userObject["isActive"].toBool();
                    loadedUsers.append(user);
                }
            }
            qDebug() << "Users loaded from users.json";
        } else {
            qWarning() << "JSON data is not an array.";
        }
    } else {
        qWarning() << "Failed to open users.json for reading:" << file.errorString();
    }
    return loadedUsers;
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("JsonSettingsApp");

    QList<User> userList;
    userList << User{"Alice", 30, true} << User{"Bob", 25, false};
    saveUsersToJson(userList);

    QList<User> loadedUsers = loadUsersFromJson();
    for (const User& user : loadedUsers) {
        qDebug() << "User:" << user.name << user.age << user.isActive;
    }

    return 0;
}

バイナリ形式で保存・読み込み (QDataStream)

Qt の QDataStream は、カスタムデータ型を含む任意のデータをバイナリ形式でシリアル化・デシリアル化するための強力なメカニズムです。これは、ファイルサイズを最小限に抑えたい場合や、パフォーマンスが重要な場合に適しています。

メリット

  • 複雑なカスタムC++オブジェクトもシリアル化可能(operator<<operator>> をオーバーロードする必要がある)。
  • ファイルサイズが小さい。
  • 高速な読み書き。

デメリット

  • Qtアプリケーション間でしか互換性が保証されない。
  • バージョン管理が重要(データ構造の変更に対応するため)。
  • 人間が読み取ることができないバイナリ形式。


#include <QCoreApplication>
#include <QFile>
#include <QDataStream>
#include <QList>
#include <QDebug>

struct Product {
    QString name;
    double price;
    int quantity;

    // QDataStream のオーバーロード (書き込み)
    friend QDataStream& operator<<(QDataStream& out, const Product& product) {
        out << product.name << product.price << product.quantity;
        return out;
    }

    // QDataStream のオーバーロード (読み込み)
    friend QDataStream& operator>>(QDataStream& in, Product& product) {
        in >> product.name >> product.price >> product.quantity;
        return in;
    }
};

void saveProductsToBinary(const QList<Product>& products) {
    QFile file("products.dat");
    if (file.open(QIODevice::WriteOnly)) {
        QDataStream out(&file);
        out.setVersion(QDataStream::Qt_5_15); // Qtのバージョンを指定 (互換性のために重要)
        out << products; // QList<T> は QDataStream が直接サポート
        file.close();
        qDebug() << "Products saved to products.dat";
    } else {
        qWarning() << "Failed to open products.dat for writing:" << file.errorString();
    }
}

QList<Product> loadProductsFromBinary() {
    QList<Product> loadedProducts;
    QFile file("products.dat");
    if (file.open(QIODevice::ReadOnly)) {
        QDataStream in(&file);
        in.setVersion(QDataStream::Qt_5_15); // 書き込み時と同じバージョンを指定
        in >> loadedProducts;
        file.close();
        qDebug() << "Products loaded from products.dat";
    } else {
        qWarning() << "Failed to open products.dat for reading:" << file.errorString();
    }
    return loadedProducts;
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QList<Product> productList;
    productList << Product{"Laptop", 1200.0, 1}
                << Product{"Mouse", 25.0, 2}
                << Product{"Keyboard", 75.0, 1};

    saveProductsToBinary(productList);

    QList<Product> loadedProducts = loadProductsFromBinary();
    for (const Product& p : loadedProducts) {
        qDebug() << "Product:" << p.name << p.price << p.quantity;
    }

    return 0;
}

注意: QList<T> は、その要素の型 TQDataStreamoperator<< および operator>> をサポートしていれば、直接シリアル化・デシリアル化できます。上記の例では Product 構造体に対してこれらの演算子をオーバーロードしています。

XML形式で保存・読み込み (QXmlStreamReader, QXmlStreamWriter / QDomDocument)

XMLも構造化されたデータを表現するための一般的な形式です。Qtは、ストリームベースのAPI (QXmlStreamReader, QXmlStreamWriter) とDOMベースのAPI (QDomDocument) の両方を提供します。ストリームベースは大規模なファイルに、DOMベースは小規模なファイルやランダムアクセスが必要な場合に適しています。

メリット

  • 異なるシステムとのデータ交換が容易。
  • 人間が読みやすい(JSONより冗長)。
  • 構造化されたデータを表現できる。

デメリット

  • 解析がJSONよりも複雑になることがある。
  • JSONよりも冗長で、ファイルサイズが大きくなる傾向がある。

データベースの利用 (SQLite, PostgreSQL, MySQLなど)

アプリケーションが大量の構造化されたデータを扱う場合、またはデータ間に複雑な関係がある場合、ローカルデータベース(例えばSQLite)やリモートデータベースの利用を検討するべきです。Qtは Qt SQL モジュールを通じてデータベースへのアクセスをサポートしています。

メリット

  • 複数のアプリケーションからの同時アクセスが可能(リモートDBの場合)。
  • データの整合性を保ちやすい。
  • 複雑なクエリやデータ操作が可能。
  • 大規模なデータの効率的な管理。
  • 追加の依存関係が必要。
  • セットアップと管理が複雑になる。
  • 大量のデータ、複雑なデータ関係、クエリ機能が必要な場合
    • データベース(Qt SQL) が最適です。
  • データのスキーマが厳密に定義されており、既存のXMLベースのシステムとの連携が必要な場合
    • XML (QXmlStreamReader/Writer または QDomDocument) を検討します。
  • パフォーマンスが最重要、ファイルサイズを最小限にしたい、Qtアプリケーション間でのみ使用する場合
    • バイナリ (QDataStream) が適しています。
  • 複雑な構造のデータ、人間が読みやすい形式、クロスプラットフォーム互換性が必要な場合
    • JSON (QJsonDocument) が最も一般的で推奨されます。
  • 簡単な設定、少量のデータ、プラットフォームネイティブなストレージが必要な場合
    • QSettings::setArrayIndex() を使うか、手動でキー名にインデックスを付けて QSettings を使う。