QSettings не делает различий между строковыми и целочисленными значениями.

Я пишу и читаю строковые и целочисленные значения, используя объект QSettings с файловой поддержкой. Когда позже я пытаюсь прочитать значения из другого процесса, значения считываются как строки, а не как int.

Это код, который я использую для записи значений:

QSettings settings("TestQSettings.ini", QSettings::IniFormat);
settings.setValue("AAA",QString("111"));
settings.setValue("BBB",222);

Это созданный файл:

[General]
AAA=111
BBB=222

Это код, который я использую для чтения значений:

QVariant qvar = settings.value("AAA");
std::cout << "AAA type " << qvar.type() << std::endl;
qvar = settings.value("BBB");
std::cout << "BBB type " << qvar.type() << std::endl;

Если я запускаю этот код из того же процесса:

AAA type 10
BBB type 2

Если я запускаю этот код из другого процесса:

AAA type 10
BBB type 10

Я знаю, что можно преобразовать типы после того, как они были прочитаны. К сожалению, это решение потребует изменения устаревшего кроссплатформенного кода Windows, который я предпочитаю не изменять, например, несколько вызовов RegQueryValueEx().

Можно ли хранить и читать информацию о типе для строк и целых чисел?

Например, строки будут иметь кавычки "", а целые числа — нет:

[General]
AAA="111"
BBB=222

Эта проблема присутствует как в Qt 4, так и в Qt 5 в Linux.


person yaronkle    schedule 14.10.2013    source источник
comment
Я не смог найти актуальный вопрос в вашем сообщении. И я боюсь, что файлы .ini не хранят информацию о типе, поэтому, если вопрос заключается в том, как сделать так, чтобы файл .ini сохранял тип, я боюсь, что ответ будет таков: вы этого не сделаете.   -  person Angew is no longer proud of SO    schedule 14.10.2013
comment
ваша программа должна знать, какого типа она должна ожидать от своего ini-файла. поэтому, если вы сохраните там int, преобразуйте QVariant в целое число, а если это не удастся, сгенерируйте исключение... просто так   -  person Zaiborg    schedule 14.10.2013
comment
Обновил вопрос. Модифицировать код не так просто, так как я делаю обертку для вызовов реестра Windows из другого проекта. Слишком много вызовов, и я не хочу слишком сильно изменять этот код.   -  person yaronkle    schedule 14.10.2013


Ответы (3)


Воу-воу, вы используете файлы .ini или реестр?

С файлами .ini, очевидно, невозможно узнать, какой это был тип, так как это все строки. Вы можете попытаться преобразовать вариант в целое число (не используйте canConvert!), и предположить, что это целое число, если оно преобразуется в единицу.

С реестром QSettings будет работать так, как вы ожидаете.

Я действительно не понимаю, в чем проблема. Не используйте файлы .ini, если хотите сохранить информацию о типе. Вы столкнетесь с точно такими же проблемами, если будете писать код вручную в зависимости от платформы.

Вы можете явно записывать строки в кавычках в файлы .ini и проверять наличие кавычек при их обратном чтении. Если кавычек нет, можно попробовать преобразовать в целое число.

person Kuba hasn't forgotten Monica    schedule 14.10.2013
comment
Я использую ini-файлы в Linux. - person yaronkle; 14.10.2013
comment
Я использую ini-файлы в Linux. Но код, который я компилирую, используется совместно с проектом Windows и должен компилироваться для обеих платформ. Я изучаю QStringList размером 1. Это выглядит многообещающе. - person yaronkle; 14.10.2013
comment
Если я добавлю кавычки с обеих сторон строки, строка будет сохранена без кавычек. - person yaronkle; 15.10.2013

Я решил эту проблему для компонента, которому необходимо сохранять и восстанавливать варианты произвольного типа, не зная, чего ожидают его клиенты. Решение состояло в том, чтобы хранить вариант typeName() рядом с каждым значением:

void store(QSettings& settings, const QString& key, const QVariant& value)
{
    settings.setValue(key+"value", value);
    settings.setValue(key+"type", value.typeName());
}

При обратном чтении мы также читаем имя типа и convert() вариант, если он еще не является правильным типом, прежде чем вернуть его.

QVariant retrieve(const QSettings& settings, const QString& key)
{
    auto value = settings.value(key+"value");
    const auto typeName = settings.value(key+"type").toString();

    const bool wasNull = value.isNull();                         // NOTE 1
    const auto t = QMetaType::type(typeName.toUtf8());           // NOTE 2

    if (value.userType() != t && !value.convert(t) && !wasNull) {
        // restore value that was cleared by the failed convert()
        value = settings.value(key+"value");
        qWarning() << "Failed to convert value" << value << "to" << typeName;
    }

    return value;
}

Примечания

  1. #P3# <блочная цитата> #P4# #P5#

  1. Неясно, является ли UTF-8 правильной кодировкой для имен QMetaType (возможно, предполагается локальная 8-битная?); все мои типы - ASCII, поэтому я просто использую вместо них toLatin1(), что может быть быстрее. Если бы это было проблемой, я бы использовал QString::fromLatin1 в методе store() (вместо неявного преобразования char* в QString), чтобы обеспечить чистое обращение туда и обратно.

    Если имя типа не найдено, t будет QMetaType::UnknownType; это нормально, потому что тогда convert() завершится ошибкой, и мы вернем непреобразованный вариант (или ноль). Это не очень хорошо, но это крайний случай, который не произойдет при обычном использовании, и моя система восстановится достаточно быстро.

person Toby Speight    schedule 25.08.2016

Оказывается, решение было очень простым.

Когда значения записываются в файл INI, тип известен. Я добавляю к значению "\"STRING прямо перед SetValue

Когда значения считываются из файла INI. Я проверяю, что строковые типы имеют указанный выше постфикс. Если они это сделают, я отрублю постфикс. Если это не так, я предполагаю, что это целые числа, а не строки.

Работает как шарм!

Спасибо вам всем и особенно @Kuba Ober за практическую раздачу решения.

person yaronkle    schedule 15.10.2013