兩個月前,簡單寫過QTextCodec中的setCodecForTr等終於消失了 (Qt5),在Qt論壇上,不少用戶都對去掉這兩個函數表示特別的不瞭解。爲什麼會這樣?我想多少能說明不少用戶對C++中源碼字符集和執行字符集的不太瞭解,從而造成對這種函數的依賴或誤用。
今天,隨着Change QString's default codec to be UTF-8進入Qt5的master分支,我們總算可以重新審視一下Qt的中文支持問題。
20120516更新:建議閱讀QtCore模塊維護者Thiago Macieira 的文章 Source code must be UTF-8 and QString wants it
沒有了setCodecXXX的Qt5
Qt5假定的執行字符集是UTF8,不再允許用戶擅自改動。這樣一來,Qt4中setCodecXXX的各種副作用不再存在,而且中文問題更爲簡單。
QString s1 = "漢語"; QString s2("漢語"); QString s3 = tr("中文") QString s4 = QStringLiteral("中文");//只要字符串不需要翻譯,請關注這個 QString s5 = QString::fromWCharArray(L"中文"); QString s6 = u8"中文";//C++11 QString s7 = tr(u8"中文") ...
所有這些在Qt5默認都會正常工作,唯一要求就是:確保你的C++的執行字符集(the execution character set)是UTF-8
各種寫法PK?
簡單不一定好
最簡單直接的用法,當屬:
QString s1 = "漢語"; QString s2("漢語"); QString s6 = u8"中文";//C++11 ...
這有什麼問題呢?
定義宏QT_NO_CAST_FROM_ASCII之後,上述代碼無法將通過編譯(對了,這個宏似乎應該改個名字纔對,叫QT_NO_CAST_FROM_CSTRING會名副其實一些)
被誤用最多的
在Qt4中,QObject::tr()是被濫用(誤用)的函數之一:
QString s3 = tr("中文") ...
原因:
在Qt4,不少用戶被鋪天蓋地的setCodecForTr()所影響,進而靠它來解決中文問題。
它的用途是用來進行翻譯(I18N和L10N)的,如果你沒有這方面的需求,真的沒必要用它。(在Qt4中,我只注意到有2個大陸網友和1個日本網友有需求並真正進行過這方面的嘗試,那麼其他應該算誤用吧?)
讓人困惑的wchar_t
剛開始接觸Qt和QString時,曾多次想過,爲什麼不用wchar_t,爲什麼,...
QString s5 = QString::fromWCharArray(L"中文");
這個東西在Windows下真的很有用:首先它是Windows系統API所用字符串,其次它和QString內部表示相同。但是由於MSVC處於種種考慮,鼓勵大家使用TEXT/_T,反倒使大家對它比較陌生。
但是從C++標準來說,wchar_t畢竟不是char16_t,所以跨平臺性不好。在linux下,這行代碼需要utf32到utf16的轉換。
QStringLiteral
這是一個宏,一個蠻複雜的宏:
QString s4 = QStringLiteral("中文");
之前?
在介紹這個宏之前,我們先看看下面寫法有什麼劣勢:
QString s1 = "漢語"; QString s2("漢語"); QString s3 = tr("中文") QString s6 = u8"中文";//C++11 ...
首先,2個漢字的字符串以UTF-8編碼的形式被編譯器放到了常量區。(至少佔7個字節吧?)
然後,程序運行時,構造QString實例,需要在堆上申請空間,存放utf16格式的相應字符串。
有沒有存在浪費?
方案
QString 內部是UTF16,如果C++編譯器在編譯期直接提供了UTF16的字符串,那麼我們在QString內部直接保存也就夠了。這樣
省掉存在兩份不同的拷貝(即相應的轉換,malloc的成本)
對漢字來說,UTF16本身就是UTF8省空間
現實
目前,我們還沒有可靠的方式在C++使用UTF16的執行字符集(the execution character set)。
儘管 L"..."(wchar_t*) 在Windows下是UTF16,但是不具備跨平臺性。
C++11可以保證這一點,u"..."(char16_t),但主流編譯器尚未提供完美支持。
這兩點,導致了QStringLiteral的複雜性
實現
源碼見 qtbase/src/corelib/tools/qstring.h
(代碼中使用宏、模板、lambda表達式,還是相當複雜的,此處只摘片段)
如果編譯器支持char16_t,則直接使用
#define QT_UNICODE_LITERAL_II(str) u"" str typedef char16_t qunicodechar; ...
否則。如果在Windows平臺下,或者在其他的wchar_t寬度爲2的環境下,使用wchar_t
#if defined(Q_CC_MSVC) # define QT_UNICODE_LITERAL_II(str) L##str #else # define QT_UNICODE_LITERAL_II(str) L"" str #endif typedef wchar_t qunicodechar; ...
否則。編譯器不支持,Qt作爲一個庫,肯定也沒有辦法
# define QStringLiteral(str) QString::fromUtf8(str, sizeof(str) - 1)