问题
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
The
wasNullvariable is in there because of this niggle ofconvert():Warning: For historical reasons, converting a null
QVariantresults in a null value of the desired type (e.g., an empty string forQString) and a result offalse.In this case, we need to ignore the misleading return value, and keep the successfully-converted null variant of the correct type.
It's not clear that UTF-8 is the correct encoding for
QMetaTypenames (perhaps local 8-bit is assumed?); my types are all ASCII, so I just usetoLatin1()instead, which might be faster. If it were an issue, I'd useQString::fromLatin1in thestore()method (instead of implicitchar*toQStringconversion), to ensure a clean round-trip.If the type name is not found,
twill beQMetaType::UnknownType; that's okay, becauseconvert()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