QSettings does not differentiate between string and int values

有些话、适合烂在心里 提交于 2019-12-12 20:26:07

问题


I am writing and reading string and int values using a file-backed QSettings object. When I later try to read the values from a different process, the values are read as strings instead of int.

This is the code I am using to write values:

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

This is the file created:

[General]
AAA=111
BBB=222

This is the code I am using to read values:

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;

If I run this code from the same process:

AAA type 10
BBB type 2

If I run this code from a different process:

AAA type 10
BBB type 10

I know it's possible to convert the types after they have been read. Unfortunately, this solution will require modifying Windows legacy cross-platform code which I prefer not to modify, for example multiple calls to RegQueryValueEx().

Is it possible to store and read the type information for strings and integers?

For example, Strings will have quotes "" and integers will not:

[General]
AAA="111"
BBB=222

This problem is present on both Qt 4 and Qt 5, on Linux.


回答1:


Whoa whoa, are you using .ini files or the registry?

With .ini files it's obviously impossible to know what the type was, since it's all a string. You can attempt conversion of the variant to an integer (don't use canConvert!), and assume it's an integer if it converts into one.

With the registry, QSettings will work as you expect it to.

I really don't see what the problem is. Don't use .ini files if you wish to retain type information. You'd face exactly the same problems if you wrote the code by hand in a platform-dependent manner.

You can explicitly write quoted strings into the .ini files, and check for presence of quotes when reading them back. If the quotes are not present, you can try conversion to an integer.




回答2:


I solved this problem for a component which needs to save and restore variants of arbitrary type, without knowing what its clients expect. The solution was to store the variant's typeName() alongside each value:

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

When reading back, we also read the type name, and convert() the variant if it's not already the correct type, before returning it.

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;
}

Notes

  1. The wasNull variable is in there because of this niggle of convert():

    Warning: For historical reasons, converting a null QVariant results in a null value of the desired type (e.g., an empty string for QString) and a result of false.

    In this case, we need to ignore the misleading return value, and keep the successfully-converted null variant of the correct type.


  1. It's not clear that UTF-8 is the correct encoding for QMetaType names (perhaps local 8-bit is assumed?); my types are all ASCII, so I just use toLatin1() instead, which might be faster. If it were an issue, I'd use QString::fromLatin1 in the store() method (instead of implicit char* to QString conversion), to ensure a clean round-trip.

    If the type name is not found, t will be QMetaType::UnknownType; that's okay, because convert() will then fail, and we'll return the unconverted variant (or a null). It's not great, but it's a corner case that won't happen in normal usage, and my system will recover reasonably quickly.




回答3:


It's obvious. QSettings uses QVariant to set and get values.

Writing, settings.setValue("BBB",222); the value is explicitly an integer for QVariant therefore the result of QVariant::type() is QVariant::Int.

Reading, INI files do not store value's type and everything is string (e.g. numbers will be read as strings too). So, the result of QVariant::type() is QVariant::String.

Don't care about types, QVariant made to transparent type differences, just convert them to integers (if it's not possible, handle it as a bad input)




回答4:


Turns out the solution was very simple.

When values are written to the INI file, the type is known. I am appending to the value "\"STRING right before SetValue

When values are read back from the INI file. I verify that string types have the above postfix. If they do, I chop the postfix off. If they don't I assume they are integers instead of strings.

Works like a charm!

Thanks to you all and especially @Kuba Ober for practically handing out the solution.



来源:https://stackoverflow.com/questions/19355112/qsettings-does-not-differentiate-between-string-and-int-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!