もう迷わない!Qt QSettings::beginReadArray()のエラーと解決策

2025-05-27

QSettings::beginReadArray() とは

QSettings::beginReadArray(const QString &prefix) は、Qtアプリケーションの設定を保存・読み込みするための QSettings クラスのメソッドの一つで、配列形式で保存された設定値を読み込む際に使用します。

QSettings は、キーと値のペアで設定を保存しますが、アプリケーションによっては同じ種類の複数の設定(例:最近開いたファイルのリスト、ユーザーアカウント情報など)を配列として保存したい場合があります。このような場合に beginReadArray() と、それに対応する beginWriteArray() が役立ちます。

動作の仕組み

beginReadArray() を呼び出すと、指定した prefix の下に配列として保存された設定の読み込みを開始します。このメソッドは、配列内の要素の数を返します。

配列内の各要素は、0から始まるインデックスで識別されます。beginReadArray() を呼び出した後、setArrayIndex(int i) メソッドを使って現在読み込む要素のインデックスを指定し、そのインデックスに対応する値を value() メソッドで読み込みます。

配列の読み込みが完了したら、必ず endArray() を呼び出して、配列モードを終了する必要があります。

使用例

例えば、複数のユーザーアカウント情報を設定ファイルに保存している場合を考えます。

設定ファイルの例(INI形式の場合)

[accounts]
size=2
0\username=user1
0\password=pass1
1\username=user2
1\password=pass2

この設定ファイルを読み込むには、以下のように QSettings::beginReadArray() を使用します。

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

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

    // アプリケーション名と組織名をQSettingsに設定
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApp");

    QSettings settings; // デフォルトの形式とスコープでQSettingsオブジェクトを作成

    // "accounts"という名前の配列の読み込みを開始
    int size = settings.beginReadArray("accounts");
    qDebug() << "Found" << size << "accounts.";

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i); // 配列のi番目の要素を選択

        QString username = settings.value("username").toString();
        QString password = settings.value("password").toString();

        qDebug() << "Account" << i << ": Username =" << username << ", Password =" << password;
    }

    settings.endArray(); // 配列の読み込みを終了

    return 0;
}
  • ネストされた配列
    QSettings は、配列の中にさらに配列をネストするような複雑な構造を直接サポートしていません。必要であれば、独自のデータ構造をシリアライズして QVariant に格納し、単一のキーとして保存する方法などを検討することになります。
  • beginWriteArray() との対
    beginReadArray() は、beginWriteArray() で書き込まれた配列を読み込むために設計されています。両者が組み合わさることで、配列形式の設定を効率的に管理できます。
  • setArrayIndex() の使用
    beginReadArray() で配列の読み込みを開始した後、配列内の個々の要素にアクセスするには、ループ内で setArrayIndex() を使って現在のインデックスを設定する必要があります。
  • endArray() の呼び出し
    beginReadArray() を呼び出したら、読み込みが完了した後、必ず endArray() を呼び出す必要があります。これを怠ると、設定オブジェクトが不正な状態になり、後続の読み書き操作に影響を与える可能性があります。


QSettings::beginReadArray() は配列形式の設定を読み込む際に非常に便利ですが、誤った使い方や設定ファイルの内容によっては意図しない結果になることがあります。以下に一般的なエラーとそのトラブルシューティングを挙げます。

beginReadArray() が常に 0 を返す(配列が見つからない)

これは最もよくある問題です。

原因

  • 設定ファイルのパスや形式の誤り
    そもそも QSettings オブジェクトが正しい設定ファイルを読み込んでいない可能性があります。
  • グループの不一致
    QSettings::beginGroup() を使用している場合、そのグループ内で配列を書き込み、読み込む必要があります。異なるグループで配列を読み込もうとすると見つかりません。
  • prefix の不一致
    beginWriteArray() で使用した prefixbeginReadArray() で使用した prefix が大文字・小文字を含め一致していない場合、配列は認識されません。
  • 配列が正しく書き込まれていない
    beginReadArray() は、QSettings::beginWriteArray() で書き込まれた形式の配列を期待します。設定ファイルに配列形式として認識できるキーがない場合、0 を返します。
    • 特に、beginWriteArray("prefix", size) を呼び出し、その後 setArrayIndex(i) を呼び出して各要素のキーを設定していない場合、QSettings はそれが配列であると認識できません。
    • INIファイルの場合、[prefix] の下に size=N というエントリと、0\key=value, 1\key=value のようなインデックス付きのキーが存在する必要があります。

トラブルシューティング

  • QSettings の初期化を確認
    • QSettings settings; のようにデフォルトコンストラクタを使用している場合、アプリケーション名と組織名が設定されていることを確認してください。
    • 特定のファイルパスや形式を指定してコンストラクタを使用している場合、そのパスと形式が正しいか確認してください。
  • QSettings::fileName() でパスを確認
    • qDebug() << settings.fileName(); を実行して、QSettings オブジェクトがどの設定ファイルにアクセスしようとしているかを確認します。意図したファイルと異なる場合、QCoreApplication::setOrganizationName()QCoreApplication::setApplicationName() の設定を見直してください。
  • prefix のスペルミスや大文字・小文字の違いを確認
    • beginReadArray("MyArray")beginWriteArray("myarray") のように、大文字・小文字が異なると認識されません。
  • 書き込みコードの確認
    • beginWriteArray() を呼び出した後、ループ内で setArrayIndex(i) を呼び出し、各要素の値を setValue() で書き込んでいるかを確認します。
    • 最後に endArray() を呼び出しているかを確認します。
  • 設定ファイルの内容を確認する
    • INI形式の場合、[prefix] の下に size=N のエントリがあるか、各要素が インデックス\キー=値 の形式で保存されているかを確認します。
    • レジストリ(Windows)やplist(macOS)の場合、Qtの内部形式で保存されているため直接確認は難しいですが、Qtのデバッグ出力や、設定ファイルのパスを確認するなどで問題がないか推測できます。

endArray() を呼び忘れる

原因

  • beginReadArray() を呼び出した後、対応する endArray() を呼び忘れると、QSettings オブジェクトが「配列読み込みモード」のままになり、その後の QSettings 操作(別のキーの読み書き、別の配列の開始など)が正しく動作しなくなる可能性があります。

トラブルシューティング

  • beginReadArray() の呼び出し後、必ず対応する endArray() を呼び出すようにします。通常は for ループの後や、配列の読み込みが完了した直後に配置します。

setArrayIndex() を呼び忘れる、または範囲外のインデックスを指定する

原因

  • setArrayIndex()beginReadArray() が返した size 以上のインデックスを指定すると、存在しない要素にアクセスしようとします。これは直接的なエラーにはなりにくいですが、value()QVariant() を返し、予期せぬ空の値になる可能性があります。
  • beginReadArray() で配列の読み込みを開始した後、for ループ内で settings.setArrayIndex(i); を呼び出すのを忘れると、常に配列の0番目の要素、または予期しないキーから値を読み込もうとします。

トラブルシューティング

  • ループの範囲を beginReadArray() が返した size の範囲内に限定するようにします。for (int i = 0; i < size; ++i) の形式が安全です。
  • 配列内の各要素を読み込むループの開始時点で、必ず setArrayIndex(i) を呼び出すようにします。

value() の型変換の失敗

原因

  • 設定ファイルに保存されている値の型と、value().toString()value().toInt() などで取得しようとしている型が一致しない場合、期待する値が得られないことがあります。例えば、数値として保存されている値を toString() で取得しようとすると、"123" のような文字列として取得されます。

トラブルシューティング

  • 値が存在しない場合のデフォルト値を指定することも有効です (settings.value("key", defaultValue).toString())。
  • QSettings から値を取得する際、そのキーに保存されている値の型を意識し、適切な QVariant の変換メソッド(toString(), toInt(), toBool(), toDouble(), toPoint(), toSize() など)を使用します。

プラットフォーム固有の挙動

原因

  • 特にINIファイル形式を使用している場合、手動でファイルを編集すると、QSettings が想定する配列形式を壊してしまうことがあります。
  • QSettings は異なるプラットフォーム(Windowsのレジストリ、macOSのplist、LinuxのINIファイルなど)で設定を保存しますが、それぞれのプラットフォームの特性や制限が影響する場合があります。
    • 例:Windowsレジストリのキー名の長さ制限、INIファイルにおけるキー名のスラッシュ/バックスラッシュの扱い(Qtはこれらをサブキーの区切り文字として解釈します)。
  • QSettings::NativeFormat と QSettings::IniFormat の違いを理解する
    移植性を考慮するなら IniFormat が良い選択肢ですが、システムによっては NativeFormat の方が効率的である場合もあります。
  • INIファイルを直接編集した場合は注意
    QSettings が生成するINIファイルの配列形式(例: 0\key=value)を理解して編集しないと、読み込み時に問題が発生します。
  • キー名に / や \ を使わない
    Qtのドキュメントにも記載がありますが、これらの文字はサブキーの区切り文字として内部的に使用されるため、キー名に含めるのは避けるべきです。


QSettings::beginReadArray() のプログラミング例

QSettings::beginReadArray() は、QSettings::beginWriteArray() で保存された配列形式の設定データを読み込むために使用されます。ここでは、書き込みと読み込みの両方の例を示し、それらがどのように連携するかを明確にします。

例1: 簡単な文字列の配列の書き込みと読み込み

この例では、ユーザーがよく使うファイルパスのリストを QSettings に保存し、後で読み込む方法を示します。

設定の書き込み(例: saveRecentFiles() 関数)

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

void saveRecentFiles() {
    // アプリケーション情報の設定(初回起動時などに設定)
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApp");

    QSettings settings; // QSettingsオブジェクトを作成

    QStringList recentFiles;
    recentFiles << "/path/to/document1.txt"
                << "/path/to/image.png"
                << "/path/to/report.pdf";

    // "recentFiles" という名前の配列の書き込みを開始
    // size を指定することで、QSettings が配列の要素数を内部で管理します。
    settings.beginWriteArray("recentFiles", recentFiles.size());

    for (int i = 0; i < recentFiles.size(); ++i) {
        settings.setArrayIndex(i); // 配列の現在のインデックスを設定
        settings.setValue("path", recentFiles.at(i)); // 各要素の"path"キーに値を設定
    }

    settings.endArray(); // 配列の書き込みを終了

    qDebug() << "Recent files saved.";
}

設定の読み込み(例: loadRecentFiles() 関数)

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

void loadRecentFiles() {
    // アプリケーション情報の設定は、書き込み時と同じである必要があります
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApp");

    QSettings settings;

    QStringList loadedRecentFiles;

    // "recentFiles" という名前の配列の読み込みを開始
    // beginReadArray() は配列の要素数を返します
    int size = settings.beginReadArray("recentFiles");
    qDebug() << "Loading" << size << "recent files...";

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i); // 配列の現在のインデックスを設定
        QString path = settings.value("path").toString(); // "path"キーの値を読み込み
        loadedRecentFiles.append(path);
    }

    settings.endArray(); // 配列の読み込みを終了

    qDebug() << "Loaded recent files:";
    for (const QString& file : loadedRecentFiles) {
        qDebug() << " - " << file;
    }
}

メイン関数での呼び出し

#include <QCoreApplication>
#include <QDebug>

// saveRecentFiles() と loadRecentFiles() は上記で定義済みとします

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

    // まず設定を保存
    saveRecentFiles();

    // 次に設定を読み込み
    loadRecentFiles();

    return 0;
}

このコードを実行すると、INIファイル(またはプラットフォームに応じた設定形式)には以下のような内容が保存されます(INI形式の場合)

[recentFiles]
size=3
0\path=/path/to/document1.txt
1\path=/path/to/image.png
2\path=/path/to/report.pdf

beginReadArray("recentFiles")size=3 を読み取り、ループで各インデックスに setArrayIndex() で移動し、それぞれの path の値を読み取ります。

例2: 構造体のようなデータの配列の書き込みと読み込み

より複雑なデータ(例: ユーザー情報)を配列として保存する場合です。

ユーザー情報を表す構造体を定義

#include <QString>

struct User {
    QString username;
    QString email;
    int id;
};

設定の書き込み(例: saveUsers() 関数)

#include <QCoreApplication>
#include <QSettings>
#include <QList>
#include <QVariant> // QVariantを使用するために必要
#include <QDebug>

// User構造体は上記で定義済みとします

void saveUsers() {
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("UserApp");
    QSettings settings;

    QList<User> users;
    users.append({"Alice", "[email protected]", 101});
    users.append({"Bob", "[email protected]", 102});
    users.append({"Charlie", "[email protected]", 103});

    settings.beginWriteArray("users", users.size());
    for (int i = 0; i < users.size(); ++i) {
        settings.setArrayIndex(i);
        settings.setValue("username", users.at(i).username);
        settings.setValue("email", users.at(i).email);
        settings.setValue("id", users.at(i).id);
    }
    settings.endArray();

    qDebug() << "User data saved.";
}

設定の読み込み(例: loadUsers() 関数)

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

// User構造体は上記で定義済みとします

void loadUsers() {
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("UserApp");
    QSettings settings;

    QList<User> loadedUsers;

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

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i);
        User user;
        user.username = settings.value("username").toString();
        user.email = settings.value("email").toString();
        user.id = settings.value("id").toInt();
        loadedUsers.append(user);
    }
    settings.endArray();

    qDebug() << "Loaded users:";
    for (const User& user : loadedUsers) {
        qDebug() << " - Username:" << user.username
                 << ", Email:" << user.email
                 << ", ID:" << user.id;
    }
}

メイン関数での呼び出し

#include <QCoreApplication>
#include <QDebug>

// saveUsers() と loadUsers() は上記で定義済みとします
// User構造体も定義済みとします

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

    saveUsers();
    loadUsers();

    return app.exec(); // QCoreApplication::exec() を呼び出してイベントループを開始(必須ではないが、アプリケーションによっては必要)
}
[users]
size=3
0\username=Alice
0\email[email protected]
0\id=101
1\username=Bob
1\email[email protected]
1\id=102
2\username=Charlie
2\email[email protected]
2\id=103
  • INI形式のファイル内容
    上記の例で示したように、INIファイルでは [prefix] の下に size=N というエントリと、インデックス\キー=値 の形式で各要素が保存されます。
  • setValue() と value()
    setValue() で値を保存し、value() で値を読み込みます。value()QVariant を返すため、適切な型変換メソッド(toString(), toInt() など)を使用する必要があります。
  • endArray()
    beginWriteArray() または beginReadArray() を呼び出した後は、必ず endArray() を呼び出して配列モードを終了する必要があります。これにより、QSettings オブジェクトが正しい状態に戻ります。
  • setArrayIndex(int i)
    配列内のどの要素を読み書きするかを指定します。これを呼び出さないと、意図しない場所から読み書きが行われる可能性があります。
  • beginReadArray("prefix")
    配列の読み込みを開始します。返り値は配列の要素数です。
  • beginWriteArray("prefix", size)
    配列の書き込みを開始します。size を指定することで、QSettings が配列の要素数を管理し、読み込み時に beginReadArray() がその数を返せるようになります。
  • QCoreApplication::setOrganizationName() と setApplicationName()
    QSettings は、これらの情報に基づいて設定ファイルの場所を決定します。アプリケーションの起動時に一度だけ設定することが推奨されます。


QSettings::beginReadArray() の代替方法

QSettings を使用し続けるが、配列をより一般的なキーで管理する

beginReadArray() を使わずに、手動でキー名にインデックスを含める方法です。

メリット

  • beginReadArray()/beginWriteArray() が提供する機能が不要な場合(例:要素数が常に固定、または独自の要素数管理ロジックがある場合)に有効。
  • QSettings のシンプルさを維持できる。

デメリット

  • endArray() のようなスコープ管理がないため、手動でキーのプレフィックスを管理する必要がある。
  • beginReadArray() が提供するような要素数の自動管理がない。

コード例

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

// 書き込み
void saveRecentFilesManually(const QStringList& files) {
    QSettings settings;
    settings.setValue("recentFiles/count", files.size()); // 要素数を別途保存

    for (int i = 0; i < files.size(); ++i) {
        settings.setValue(QString("recentFiles/%1/path").arg(i), files.at(i));
    }
    qDebug() << "Recent files saved manually.";
}

// 読み込み
QStringList loadRecentFilesManually() {
    QSettings settings;
    QStringList loadedFiles;

    int count = settings.value("recentFiles/count", 0).toInt(); // 要素数を読み込み
    for (int i = 0; i < count; ++i) {
        QString path = settings.value(QString("recentFiles/%1/path").arg(i)).toString();
        loadedFiles.append(path);
    }
    qDebug() << "Recent files loaded manually:";
    for (const QString& file : loadedFiles) {
        qDebug() << " - " << file;
    }
    return loadedFiles;
}

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

    QStringList myFiles;
    myFiles << "fileA.txt" << "fileB.doc";
    saveRecentFilesManually(myFiles);
    loadRecentFilesManually();

    return 0;
}

JSON (JavaScript Object Notation) を使用する

Qt は QJsonDocument, QJsonObject, QJsonArray を使用して JSON データの読み書きを強力にサポートしています。複雑な階層構造を持つデータを扱うのに非常に適しており、人間が読みやすく、Web API との連携にもよく使われます。

メリット

  • Qt の JSON モジュールは非常に使いやすい。
  • 多くの言語やプラットフォームでサポートされており、相互運用性が高い。
  • 人間が読み書きしやすいテキスト形式。
  • 複雑なデータ構造(ネストされたオブジェクトや配列)を自然に表現できる。

デメリット

  • 大量の小さなデータを扱う場合、パース(解析)のオーバーヘッドが発生する可能性がある。
  • バイナリ形式に比べてファイルサイズが大きくなる傾向がある。

コード例

#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QDebug>

struct User {
    QString username;
    QString email;
    int id;
};

// 書き込み
void saveUsersToJson(const QList<User>& users) {
    QJsonArray userArray;
    for (const User& user : users) {
        QJsonObject userObject;
        userObject["username"] = user.username;
        userObject["email"] = user.email;
        userObject["id"] = user.id;
        userArray.append(userObject);
    }

    QJsonDocument doc(userArray);
    QFile file("users.json");
    if (file.open(QIODevice::WriteOnly)) {
        file.write(doc.toJson(QJsonDocument::Indented)); // 整形して書き込み
        file.close();
        qDebug() << "User data saved to users.json (JSON format).";
    } else {
        qWarning() << "Failed to open users.json for writing.";
    }
}

// 読み込み
QList<User> loadUsersFromJson() {
    QList<User> loadedUsers;
    QFile file("users.json");
    if (!file.open(QIODevice::ReadOnly)) {
        qWarning() << "Failed to open users.json for reading.";
        return loadedUsers;
    }

    QByteArray jsonData = file.readAll();
    file.close();

    QJsonDocument doc = QJsonDocument::fromJson(jsonData);
    if (doc.isNull()) {
        qWarning() << "Failed to parse JSON from users.json.";
        return loadedUsers;
    }

    if (doc.isArray()) {
        QJsonArray userArray = doc.array();
        for (const QJsonValue& value : userArray) {
            if (value.isObject()) {
                QJsonObject userObject = value.toObject();
                User user;
                user.username = userObject["username"].toString();
                user.email = userObject["email"].toString();
                user.id = userObject["id"].toInt();
                loadedUsers.append(user);
            }
        }
    }
    qDebug() << "User data loaded from users.json (JSON format):";
    for (const User& user : loadedUsers) {
        qDebug() << " - Username:" << user.username << ", Email:" << user.email << ", ID:" << user.id;
    }
    return loadedUsers;
}

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

    QList<User> usersToSave;
    usersToSave.append({"Alice", "[email protected]", 101});
    usersToSave.append({"Bob", "[email protected]", 102});
    saveUsersToJson(usersToSave);

    loadUsersFromJson();

    return 0;
}

XML (Extensible Markup Language) を使用する

Qt には QXmlStreamReaderQXmlStreamWriter といったストリームベースの XML 処理クラスがあります。DOM ベースの QDomDocument もありますが、通常はストリームベースの方が効率的です。

メリット

  • 人間が読みやすいテキスト形式(JSONほど簡潔ではない)。
  • スキーマ定義などにより、データの厳密な構造を定義しやすい。
  • 階層的なデータを表現できる。

デメリット

  • 書き込み・読み込みのコードがJSONよりも複雑になりやすい。
  • パースのオーバーヘッドが大きくなることがある。
  • JSONに比べて冗長になりがちで、ファイルサイズが大きくなる。

QDataStream を使用する (バイナリシリアライズ)

QDataStream は、Qt の型やカスタム型をバイナリ形式でファイルやネットワークストリームにシリアライズ(直列化)するための強力な手段です。

メリット

  • Qtアプリケーション間での相互運用性が高い。
  • 任意のリッチなデータ型を保存可能
    QVector, QList, QString, QImage, QPoint, QRect など、ほとんどの Qt 型が直接サポートされており、カスタム型も operator<<operator>> をオーバーロードすることで簡単にシリアライズできる。
  • 高速
    読み書きのパフォーマンスが高い。
  • 非常にコンパクト
    ファイルサイズが小さくなる。

デメリット

  • Qt以外の環境との相互運用性は低い。
  • 異なるQtバージョン間での互換性に注意が必要な場合がある。 (ストリームのバージョンを設定することで解決できることが多い)
  • 人間が読み書きできないバイナリ形式。

コード例

#include <QCoreApplication>
#include <QDataStream>
#include <QFile>
#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;
    }
    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);
        // ストリームバージョンを設定することで、将来のQtバージョンとの互換性を確保
        out.setVersion(QDataStream::Qt_5_15); // 例: Qt 5.15 のバージョンを使用
        out << products; // QList<Product> 全体を一度に書き込み
        file.close();
        qDebug() << "Product data saved to products.dat (Binary format).";
    } else {
        qWarning() << "Failed to open products.dat for writing.";
    }
}

// 読み込み
QList<Product> loadProductsFromBinary() {
    QList<Product> loadedProducts;
    QFile file("products.dat");
    if (!file.open(QIODevice::ReadOnly)) {
        qWarning() << "Failed to open products.dat for reading.";
        return loadedProducts;
    }

    QDataStream in(&file);
    in.setVersion(QDataStream::Qt_5_15); // 書き込み時と同じバージョンを設定
    in >> loadedProducts; // QList<Product> 全体を一度に読み込み
    file.close();

    qDebug() << "Product data loaded from products.dat (Binary format):";
    for (const Product& p : loadedProducts) {
        qDebug() << " - Name:" << p.name << ", Price:" << p.price << ", Quantity:" << p.quantity;
    }
    return loadedProducts;
}

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

    QList<Product> productsToSave;
    productsToSave.append({"Laptop", 1200.0, 5});
    productsToSave.append({"Mouse", 25.50, 20});
    saveProductsToBinary(productsToSave);

    loadProductsFromBinary();

    return 0;
}

カスタムのファイル形式 / 独自のパーサー

非常に特定の要件がある場合や、既存の形式では効率が悪い、または制御が足りない場合に検討されます。

メリット

  • 既存のデータ形式との統合が必要な場合に有効。
  • 完全に制御可能で、パフォーマンスやファイルサイズを最適化できる。
  • 車輪の再発明になることが多い。
  • エラー処理や互換性の維持が複雑になる。
  • 開発コストが高い。
  • 上記全てで対応できない特殊な要件がある場合
    カスタム形式
  • スキーマ定義の厳密さ、既存のXMLシステムとの連携が必要な場合
    XML (QXmlStreamReader/Writer)
  • データ構造が明確で、ファイルサイズや読み書き速度が最優先、Qtアプリケーション間でのみデータをやり取りする場合
    バイナリ形式 (QDataStream)
  • 複雑な階層構造、人間が読み書きできる形式、他のシステムとの相互運用性を重視する場合
    JSON (QJsonDocument など)
  • 簡単なキー-値ペア、OS標準の設定メカニズムを利用したい、配列の要素数が比較的少ない場合
    QSettings (beginReadArray() を含む、または手動管理)