QSettings的编码问题(QTBUG15543、QTBUG19552)

    技术2022-06-12  104

    我们在QSettings 与中文 一文中认识了QSettings在Qt4.5中引入的setIniCodec成员函数。

    这是一个挺不错的函数,估计解决了不少人的中文(我自己几乎不在里面用中文,感受不是太深)。

    这两天看Qt的bugreport,发现这个看似无害的便利函数还是直接导致了一些 bug:

    QTBUG15543

    是一个这样的问题:

    一个长度为256的QByteArray对象(存放"/x0/x1/x2.../x255")将该对象通过设置了编码的QSettings写入ini文件后,内容不对! 当然更无法正确读入了 如果不设置编码的话,写入和读出的内容是一致的(也就是工作正常)。

    bug提交者给出的代码如下:

    #include <QtCore/QCoreApplication> #include <QSettings> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); { QSettings set("settings.ini",QSettings::IniFormat,0); set.setIniCodec("cp1251"); QByteArray ba; ba.resize(256); for(int i=0;i<ba.size();i++){ ba[i]=i; } set.setValue("array",ba); } { QSettings set("settings.ini",QSettings::IniFormat,0); set.setIniCodec("cp1251"); QByteArray ba=set.value("array").toByteArray(); qDebug()<<"size="<<ba.size(); for(int i=0;i<ba.size();i++){ if((uchar)ba.at(i)!=i){ qDebug()<<"Error: "<<i<<"!="<<(uchar)ba.at(i); break; } } } return 0; }

    原因

    我们在QSettings 与中文 一文中涉及到这部分内容。

    QSettings 的核心是 一个QMap<QString, QVariant> ,key是一个QString,value是QVariant类型

    将value写入文件的过程分两步 将 QVariant 变成 QString 或 QStringList将字符串和字符串列表序列化(字符转义、解码等)得到QByteArray字节流

    回头看这个bug:

    QByteArray 对象存放在QVariant中,第一步先变成QString:@ByteArray(.....)

    括号内是QByteArray的真实内容"/x0/x1/x2.../x255" 对应的字符串。其实这是通过QString::fromLatin1()实现的

    将该字符串转义并采用指定的codec进行编码

    问题出来了:cp1251 无法对U+128U+129 等unicode字符进行解码,于是问号被写入了ini文件。

    bug的修复:

    这个应该还算比较简单,只要保证对于QByteArray不进行编码即可(当然,对于其他一些自定义的类型,会变成字符串Variant(...),也不能进行编码)

    QTBUG-19552

    是这样一个问题:

    用未设置codec的QSettings读入一个ini文件销毁该 QSettings 对象用设置了codec的QSettings重现读入该ini文件设置的编码不起作用

    bug 提交者给出的代码如下

    #include <QtCore/QCoreApplication> #include <QtCore/QSettings> #include <QtCore/QString> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QString v; { QSettings settings("test1.ini", QSettings::IniFormat); v = settings.value("lang").toString(); } QSettings settings1("test1.ini", QSettings::IniFormat); settings1.setIniCodec("UTF-8"); QString v1 = settings1.value("lang").toString(); return 0; }

    原因

    QSettings 应该是处于性能上的考虑,会对读入的ini文件内容进行缓存。

    这个bug产生的原因是:

    上一个对象销毁时,读入的ini的内容被缓存了新创建QSettings对象时,发现缓存中有数据,不会再读取文件。于是编码将不会起作用。

    如何修复:不太清楚该如何做,或许

    禁用掉这个缓存,这个是最简单的,修改的代码比较少。只要QSettings销毁时不将配置文件内容放入 unusedCache 这个全局静态的QCache对象即可。当设置 codecs 的时候,设置一个dirty 标记,这样一来,在使用QSettings时可以强制读取文件。

    参考

    http://bugreports.qt.nokia.com/browse/QTBUG-15543

    http://bugreports.qt.nokia.com/browse/QTBUG-19552

     


    最新回复(0)