QString亂談(2) &QT 5 中文亂碼問題

 

QString亂談(2)

  • 長期以來,不少人都清楚,一旦C++源碼中直接使用了中文,這樣的源碼想要跨平臺(I18N)會很是困難。linux

隨着:windows

  • Windows下:MSVC2010成爲主流ide

  • Linux下:GCC升級到4.6函數

C++中的中文問題 纔算有了一個比較優雅的、跨平臺的Workaround。編碼

(本文討論編譯器範圍:GCC4.6+, MSVC2010sp1+ 。本文屬於QString系列,但暫不涉及QString)spa

C++ 中文問題

要在C++中正確使用中文,必需要了解下面兩個概念:.net

源碼字符集(the source character set)命令行

源碼文件是使用何種編碼保存的翻譯

執行字符集(the execution character set)code

可執行程序內保存的是何種編碼(程序執行時內存中字符串編碼)

C++98的問題: 既沒有規定源碼字符集,也沒有規定執行字符集

這個... 如何理解?不妨看個例子

例子

這個要求高麼?

  • 一個簡單的C++程序,只是但願它能在簡體中文Windows、正體中文Windows、英文版Windows、Linux、MAC OS...下的結果一致。

//main.cppint main(){    char mystr[] = "老老實實的學問,來不得半點馬虎";    return sizeof mystr;}

能夠試着反問本身兩個問題

  • 這個源碼文件是何種編碼保存的?(有肯定答案麼?)

  • mystr中是什麼內容?(有肯定答案麼?)

對C++來講,這兩個都不肯定。

  • 固定平臺的話,還能忍忍

  • 要跨平臺的話,這種東西...

GCC

在GCC下,這兩個均可以使用你本身喜愛的編碼(若是不指定,默認都是UTF8)

-finput-charset=charset-fexec-charset=charset

除了前兩個選項外,還有一個:

-fwide-exec-charset=charset

wide? 不妨先猜一下它是幹嗎的

MSVC

MSVC沒有相似前面的選項。

源碼字符集如何解決?

有BOM麼,有則按BOM解釋,無則使用本地Locale字符集(隨系統設置而變)

執行字符集如何解決?

使用本地Locale字符集(隨系統設置而變)

挺霸道哈(固然,源碼中可使用#pragma setlocale("..."),但功能頗有限,好比Windows沒有utf8的locale,因此...)。

另外,和GCC對應的wide-exec-charset呢?

寬執行字符集如何解決?

不妨先考慮一下

怎麼辦?

這才兩個編譯器,看起來就這麼複雜了。而C++編譯器的數目遠大於2.

要想跨平臺,必須確保這兩個字符集都是「肯定」的,而能勝任該任務的字符集,彷佛理想的也只能是...

UTF-8方案

  • 若是咱們將源碼保存成utf8,執行字符集也選爲utf8,那麼天下將太平了。使用非ASCII字符的源碼文件也就能夠在不一樣國家的用戶間無障礙流通了 ;-).

源碼保存成UTF-8沒有什麼困難,可是,執行字符集須要是UTF-8。沒那麼簡單

對GCC來講,這個問題很簡單(默認的編碼選項足夠了):

  • 只要源碼文件保存成utf8便可(帶或不帶BOM都可)

  • 早期的gcc不接收帶BOM的utf8源碼文件,如今,至少在GCC4.6中,這一限制再也不存在。

對MSVC來講,這個問題異常複雜:

  • 對MSVC2003來講,只要源碼保存成不帶BOM的utf8便可

  • 對MSVC200五、(沒在SP1基礎上裝熱補丁的)MSVC2008來講。徹底沒辦法

  • 直到MSVC2010sp1,纔算提供了一個解決方案。源碼保存成帶BOM的utf8,utf16,...,而後添加

#pragma execution_character_set("utf-8")

要想跨GCC4.6+和MSVC2010sp1+,咱們須要取它們的交集:也就是

  • 源碼保存成帶BOM的utf8

  • 爲MSVC添加#pragma

//main.cpp#if _MSC_VER >= 1600#pragma execution_character_set("utf-8")#endifint main(){    char mystr[] = "老老實實的學問,來不得半點馬虎";    return sizeof mystr;}

C++11

等到MSVC支持C++11的String Literals之時,咱們就不必用那個蹩腳的pragma了,直接

    char mystr[] = u8"老老實實的學問,來不得半點馬虎";

便可(儘管如今在GCC下沒問題,但要跨平臺,估計要等到Visual C++ 12了)。

有個問題?

C++98中不是有個wchar_t麼,它不是用來表示unicode字符的麼?

Unicode 4.0標準的5.2節是如何說的:

The width of wchar_t is compiler-specific and can be as small as 8 bits. Consequently, programs that need to be portable across any C or C++ compilershould not use wchar_t for storing Unicode text. The wchar_t type is intended forstoring compiler-defined wide characters, which may be Unicode characters in some compilers.

在回頭看看GCC的選項

-fwide-exec-charset=charset

儘管GCC爲其提供的默認編碼是UTF16或UTF32(取決於wchar_t的寬度),但該編碼是能夠隨意設置的。

儘管這個東西不保證跨平臺,也很很差玩, 可是,因爲在windows下面wchar_t用來表示utf16字符,並且直接對應系統API接口,因此在類型char16_t普及以前,仍是很重要的。

C++11執行字符集

前面提到的u8就是C++11爲「執行字符集」所作的努力之一。

新明確規定了utf八、utf16和utf32這3種執行字符集。

char*

u8"中文"

char16_t*

u"中文"

char32_t*

U"中文"

但是C++11並無規定源碼字符集

const char* mystr=u8"中文";

C++標準對編譯器說,我無論這個文件的具體編碼是什麼,但你必須給我生成對應utf8編碼的字節流。

編譯器彷佛有點傻了吧?不知道源文件的編碼,我如何轉換

因而:

MSVC說:源碼文件必須有BOM,否則我就認爲你是本地locale的編碼

GCC說:我認爲你就是utf8編碼,除非經過命令行通知我其餘編碼

在C++11標準下,對源碼編碼 簡單的處理辦法仍是,使用帶BOM的UTF8保存。

第二篇

今天,隨着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++11QString 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"" strtypedef 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#endiftypedef wchar_t qunicodechar;...

  • 不然。編譯器不支持,Qt做爲一個庫,確定也沒有辦法

# define QStringLiteral(str) QString::fromUtf8(str, sizeof(str) - 1)
相關文章
相關標籤/搜索