问题
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
wasNull
variable is in there because of this niggle ofconvert()
:Warning: For historical reasons, converting a null
QVariant
results 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
QMetaType
names (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::fromLatin1
in thestore()
method (instead of implicitchar*
toQString
conversion), to ensure a clean round-trip.If the type name is not found,
t
will 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