Введение
Конфигурационные файлы — неотъемлемая часть большинства приложений. Qt из коробки прекрасно поддерживает формат INI через QSettings, но всё больше проектов выбирают TOML или YAML за читаемость и выразительность. Разберём, какие средства доступны в экосистеме Qt для каждого формата и когда какой выбрать.
Концепция
- INI — нативно поддерживается через
QSettingsс форматомQSettings::IniFormat. Простой, давно стандартизированный, но ограниченный формат: плоская иерархия секций, нет встроенной поддержки списков сложных структур и типов данных без дополнительных соглашений.
- JSON — поддерживается нативно через
QJsonDocument,QJsonObject,QJsonArray(модуль QtCore). Хорошо подходит, если конфигурация структурирована иерархически и не требует комментариев (JSON их не поддерживает).
- YAML — нет встроенной поддержки в Qt. Требуются сторонние библиотеки, например
yaml-cpp(на C++) с собственноручной прослойкой конвертации вQVariant/QJsonValue.
- TOML — также нет встроенной поддержки; используются библиотеки
toml++(header-only, современный C++17) илиcpptoml.
Пример кода: Чтение конфигов в
Сравним три подхода на одной и той же задаче — чтении конфигурации сервера (хост, порт, список разрешённых пользователей).
1. QSettings (INI)
#include <QSettings>
#include <QDebug>
void loadIniConfig()
{
QSettings settings("server.ini", QSettings::IniFormat);
settings.beginGroup("network");
QString host = settings.value("host", "127.0.0.1").toString();
int port = settings.value("port", 8080).toInt();
settings.endGroup();
QStringList users = settings.value("auth/allowed_users").toStringList();
qDebug() << "INI:" << host << port << users;
}
Содержимое server.ini:
[network]
host=192.168.1.10
port=9090
[auth]
allowed_users=admin, operator, guest
2. QJsonDocument (JSON)
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>
void loadJsonConfig()
{
QFile file("server.json");
if (!file.open(QIODevice::ReadOnly))
return;
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError) {
qWarning() << "Ошибка парсинга JSON:" << error.errorString();
return;
}
QJsonObject root = doc.object();
QJsonObject network = root["network"].toObject();
QString host = network.value("host").toString("127.0.0.1");
int port = network.value("port").toInt(8080);
QJsonArray usersArray = root["auth"].toObject()["allowed_users"].toArray();
QStringList users;
for (const QJsonValue &v : usersArray)
users << v.toString();
qDebug() << "JSON:" << host << port << users;
}
3. toml++ (TOML), требует внешней библиотеки
#include <toml++/toml.h>
#include <QDebug>
#include <QString>
void loadTomlConfig()
{
toml::table config;
try {
config = toml::parse_file("server.toml");
} catch (const toml::parse_error &err) {
qWarning() << "Ошибка парсинга TOML:" << err.description().data();
return;
}
QString host = QString::fromStdString(
config["network"]["host"].value_or(std::string("127.0.0.1")));
int port = config["network"]["port"].value_or(8080);
QStringList users;
if (auto arr = config["auth"]["allowed_users"].as_array()) {
for (auto &&el : *arr) {
if (auto s = el.value<std::string>())
users << QString::fromStdString(*s);
}
}
qDebug() << "TOML:" << host << port << users;
}
Содержимое server.toml:
[network]
host = "192.168.1.10"
port = 9090
[auth]
allowed_users = ["admin", "operator", "guest"]
Пояснения к коду
QSettings автоматически кеширует значения и умеет писать конфиги обратно тем же API (setValue()), что делает её удобной для пользовательских настроек, изменяемых самим приложением. JSON и TOML обычно используются как «читай-только» конфигурация, поставляемая вместе с приложением или редактируемая администратором вручную, поэтому запись назад реализуется отдельно через сериализацию объекта целиком.
Подводные камни
QSettingsна Windows может писать в реестр. Если явно не указатьQSettings::IniFormat, на Windows по умолчанию используется системный реестр (QSettings::NativeFormat), что неожиданно для разработчиков, привыкших к Linux/macOS, гдеNativeFormatсоответствует INI-подобным файлам.
- Типы в INI всегда текстовые.
QSettingsхранит и читает значения какQVariant, но физически в INI-файле всё представлено текстом — при ручном редактировании файла легко внести типовую ошибку (например, записать список без запятых), которая будет молча неверно интерпретирована.
- JSON не поддерживает комментарии. Если конфигурацию предполагается редактировать людьми (а не только программой), отсутствие комментариев в JSON — серьёзное неудобство по сравнению с TOML или YAML.
- Подключение сторонних библиотек усложняет сборку.
toml++header-only и легко подключается, аyaml-cppтребует сборки и линковки — это увеличивает сложность CI и сборочных скриптов, особенно при кросс-компиляции для мобильных платформ.
- Отсутствие единообразной обработки ошибок.
QJsonDocument::fromJson()возвращает структуру ошибки через выходной параметр, аtoml++бросает исключения — при смешивании форматов в одном проекте код обработки ошибок должен быть продуман для каждого случая отдельно.
- YAML особенно подвержен скрытым ошибкам типизации. Известная проблема YAML — автоматическое приведение строк типа
no,yes,on,offк булевым значениям, что может неожиданно сломать конфигурацию (например, страна с кодом «NO» превратится вfalse). При использовании YAML стоит явно квотировать строковые значения, которые могут совпасть со специальными литералами.
Выбор формата конфигурации в Qt-проекте — это компромисс между встроенной поддержкой (INI, JSON) и удобством для человека (TOML, YAML), требующим внешних зависимостей. Для большинства внутренних настроек приложения достаточно QSettings, а для сложных конфигураций, читаемых и редактируемых разработчиками или DevOps-инженерами, разумно подключить toml++.
================================================================================