1. 程式人生 > >QSettings 與中文(反斜槓 轉義)

QSettings 與中文(反斜槓 轉義)

本文介紹的是QSettings的 IniFormat 截止目前(Qt4.7)對中文的支援情況,如果你想實現自己定義格式(比如"[中文] 中文=中文"這樣),可參看QSettings 自定義格式Qt4.4(包含)之前

先看個例子:

#include <QtCore/QCoreApplication>
#include <QtCore/QSettings>

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QString value = QString::fromLocal8Bit("我是漢字");
QSettings settings("config.ini", QSettings::IniFormat);
settings.setValue("setcion/key", value);

return 0;
}

結果如下:

[setcion]
key=\x6211\x662f\x6c49\x5b57

這兒Value部分顯示的是上面4個漢字的UCS2(即UTF-16)編碼。如果你喜歡,你可以叫它“亂碼”。

為什麼四個漢字變成這個樣子了呢?

因為4個漢字的Unicode編碼超出了ASCII碼的範圍,而將其序列化的話,有很多種不同的方案,而這些不同的方案中,對特定的人來說,只有一種方案是最合適的,其他的可能都被這特定的人稱作亂碼。

眾口難調啊?怎麼辦,那就不調了唄。直接給出UTF-16編碼,這樣一來,儘管每個人都不太喜歡,但總比讓多數人都掃興要好得多。

Qt4.5的轉變

從Qt4.5開始,QSettings提供了一個新的成員函式,setIniCodec。這樣一來,各種非ASCII碼的使用者應該高興一點了,喜歡什麼編碼自己來設定,而不用考慮什麼國際使用者。

對簡體中文使用者來說,GBK還是UTF-8任意選擇一個自己喜歡的就行了:

#include <QtCore/QCoreApplication>
#include <QtCore/QSettings>

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QString value = QString::fromLocal8Bit("我是漢字");
QSettings settings("config.ini", QSettings::IniFormat);
#ifdef LOVE_GBK
settings.setIniCodec("GB2312");
#else
settings.setIniCodec("UTF8");
#endif
settings.setValue("setcion/key", value);

return 0;
}

結果如下:

[setcion]
key=我是漢字Key中的中文

截止到目前的Qt4.6.3,Key 和Section的中文仍不能使用本地編碼,比如:

#include <QtCore/QCoreApplication>
#include <QtCore/QSettings>
#include <QtCore/QStringBuilder>

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QString section = QString::fromLocal8Bit("節");
QString key = QString::fromLocal8Bit("鍵");
QString value = QString::fromLocal8Bit("我是漢字");
QSettings settings("config.ini", QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(section%"/"%key, value);

return 0;
}

注意:此處字串連線用的Qt4.6引入的%,如果是之前的版本,換成+並刪除相應標頭檔案即可。

結果如下:

[%U8282]
%U952E=我是漢字

同一開始的Value一樣,Key和Section中非ASCII字元的仍會進行轉義,此處我們看到的就是%U後跟UTF-16的數值。該部分不受 setIniCodec的影響。

不止中文這樣

其實不止中文是這樣,Qt對傳統的Latin系列編碼也沒有特殊的照顧

當Section Key Value中出現Latin字元時,同樣將進行轉義。比如:

#include <QtCore/QCoreApplication>
#include <QtCore/QSettings>
#include <QtCore/QStringBuilder>

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
QString section = QString::fromLocal8Bit("ÀÁ");
QString key = QString::fromLocal8Bit("ÀÁ");
QString value = QString::fromLocal8Bit("ÀÁ");
QSettings settings("config.ini", QSettings::IniFormat);
settings.setValue(section%"/"%key, value);

return 0;
}

結果:

[%E0%E1]
%E0%E1=\xe0\xe1Settings原始碼

%QTDIR%\src\corelib\io\qsettings.cpp

QSettings的資料在記憶體中存放於 QMap<QString, QVariant> 中,讀寫檔案也就是將檔案讀入該Map和從該Map寫入檔案的過程。

ini檔案的寫入

bool QConfFileSettingsPrivate::writeIniFile(QIODevice &device, const ParsedSettingsMap &map
)

Key的寫入相對比較簡單,因為本身就是字串,只需序列化即可(對'0'-'9', 'a'-'z', 'A'-'Z', '_', '-', '.'之外的字元進行轉義處理)。

void QSettingsPrivate::iniEscapedKey(const QString &key, QByteArray &result)

Value的寫入就很複雜了,分兩步走:

  • 將 QVariant 變成 QString 或 QStringList
    • StringList 和 List 型 ==> QStringList,其他轉成QString

    • 直接轉成字串的 String LongLong Int Bool Double ...

    • 包含可讀資訊但不可直接轉的 Rect Size Point ...
    • 包含非可讀資訊的 ByteArray

    • Invalid 型的 QVariant
    • 其他 QVariant
  • 將字串序列化
    • 主要是對字元的Escape,比如'\0','\a'等控制字元,變成"\\0", "\\a"等
    • 其他控制字元,"\\x.."
    • 大於 7F 的字元,如有codec,則使用codec,如無,則用"\\x...."
    • 字串內包含';', ',', '='時,字串兩段加引號
    • 字元'\0', "\x.."之後如果是數字(0-9a-fA-F),要繼續如此處理