溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

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

發(fā)布時間:2020-07-24 13:52:45 來源:網(wǎng)絡(luò) 閱讀:2367 作者:WZM3558862 欄目:系統(tǒng)運維

 

QString亂談(2)

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

隨著:

  • Windows下:MSVC2010成為主流

  • Linux下:GCC升級到4.6

C++中的中文問題 才算有了一個比較優(yōu)雅的、跨平臺的Workaround。

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

C++ 中文問題

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

源碼字符集(the source character set)

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

執(zhí)行字符集(the execution character set)

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

C++98的問題: 既沒有規(guī)定源碼字符集,也沒有規(guī)定執(zhí)行字符集

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

例子

這個要求高么?

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

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

可以試著反問自己兩個問題

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

  • mystr中是什么內(nèi)容?(有確定答案么?)

對C++來說,這兩個都不確定。

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

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

GCC

在GCC下,這兩個都可以使用你自己喜好的編碼(如果不指定,默認(rèn)都是UTF8)

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

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

-fwide-exec-charset=charset

wide? 不妨先猜一下它是干嘛的

MSVC

MSVC沒有類似前面的選項。

源碼字符集如何解決?

有BOM么,有則按BOM解釋,無則使用本地Locale字符集(隨系統(tǒng)設(shè)置而變)

執(zhí)行字符集如何解決?

使用本地Locale字符集(隨系統(tǒng)設(shè)置而變)

挺霸道哈(當(dāng)然,源碼中可以使用#pragma setlocale("..."),但功能很有限,比如Windows沒有utf8的locale,所以...)。

另外,和GCC對應(yīng)的wide-exec-charset呢?

寬執(zhí)行字符集如何解決?

不妨先考慮一下

怎么辦?

這才兩個編譯器,看起來就這么復(fù)雜了。而C++編譯器的數(shù)目遠大于2.

要想跨平臺,必須確保這兩個字符集都是“確定”的,而能勝任該任務(wù)的字符集,似乎理想的也只能是...

UTF-8方案

  • 如果我們將源碼保存成utf8,執(zhí)行字符集也選為utf8,那么天下將太平了。使用非ASCII字符的源碼文件也就可以在不同國家的用戶間無障礙流通了 ;-).

源碼保存成UTF-8沒有什么困難,但是,執(zhí)行字符集需要是UTF-8。沒那么簡單

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

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

  • 早期的gcc不接收帶BOM的utf8源碼文件,現(xiàn)在,至少在GCC4.6中,這一限制不再存在。

對MSVC來說,這個問題異常復(fù)雜:

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

  • 對MSVC2005、(沒在SP1基礎(chǔ)上裝熱補丁的)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[] = "老老實實的學(xué)問,來不得半點馬虎";    return sizeof mystr;}

C++11

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

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

即可(盡管現(xiàn)在在GCC下沒問題,但要跨平臺,估計要等到Visual C++ 12了)。

有個問題?

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

Unicode 4.0標(biāo)準(zhǔn)的5.2節(jié)是如何說的:

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為其提供的默認(rèn)編碼是UTF16或UTF32(取決于wchar_t的寬度),但該編碼是可以隨意設(shè)置的。

盡管這個東西不保證跨平臺,也很不好玩, 但是,由于在windows下面wchar_t用來表示utf16字符,而且直接對應(yīng)系統(tǒng)API接口,所以在類型char16_t普及之前,還是很重要的。

C++11執(zhí)行字符集

前面提到的u8就是C++11為“執(zhí)行字符集”所做的努力之一。

新明確規(guī)定了utf8、utf16和utf32這3種執(zhí)行字符集。

char*

u8"中文"

char16_t*

u"中文"

char32_t*

U"中文"

可是C++11并沒有規(guī)定源碼字符集

const char* mystr=u8"中文";

C++標(biāo)準(zhǔn)對編譯器說,我不管這個文件的具體編碼是什么,但你必須給我生成對應(yīng)utf8編碼的字節(jié)流。

編譯器似乎有點傻了吧?不知道源文件的編碼,我如何轉(zhuǎn)換

于是:

MSVC說:源碼文件必須有BOM,不然我就認(rèn)為你是本地locale的編碼

GCC說:我認(rèn)為你就是utf8編碼,除非通過命令行通知我其他編碼

在C++11標(biāo)準(zhǔn)下,對源碼編碼 簡單的處理辦法還是,使用帶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假定的執(zhí)行字符集是UTF8,不再允許用戶擅自改動。這樣一來,Qt4中setCodecXXX的各種副作用不再存在,而且中文問題更為簡單。

QString s1 = "漢語";QString s2("漢語");QString s3 = tr("中文")QString s4 = QStringLiteral("中文");//只要字符串不需要翻譯,請關(guān)注這個QString s5 = QString::fromWCharArray(L"中文");QString s6 = u8"中文";//C++11QString s7 = tr(u8"中文")...

所有這些在Qt5默認(rèn)都會正常工作,唯一要求就是:確保你的C++的執(zhí)行字符集(the execution character set)是UTF-8

各種寫法PK?

簡單不一定好

最簡單直接的用法,當(dāng)屬:

QString s1 = "漢語";QString s2("漢語");QString s6 = u8"中文";//C++11...

這有什么問題呢?

  • 定義宏QT_NO_CAST_FROM_ASCII之后,上述代碼無法將通過編譯(對了,這個宏似乎應(yīng)該改個名字才對,叫QT_NO_CAST_FROM_CSTRING會名副其實一些)

被誤用最多的

在Qt4中,QObject::tr()是被濫用(誤用)的函數(shù)之一:

QString s3 = tr("中文")...

原因:

  • 在Qt4,不少用戶被鋪天蓋地的setCodecForTr()所影響,進而靠它來解決中文問題。

它的用途是用來進行翻譯(I18N和L10N)的,如果你沒有這方面的需求,真的沒必要用它。(在Qt4中,我只注意到有2個大陸網(wǎng)友和1個日本網(wǎng)友有需求并真正進行過這方面的嘗試,那么其他應(yīng)該算誤用吧?)

讓人困惑的wchar_t

剛開始接觸Qt和QString時,曾多次想過,為什么不用wchar_t,為什么,...

QString s5 = QString::fromWCharArray(L"中文");

這個東西在Windows下真的很有用:首先它是Windows系統(tǒng)API所用字符串,其次它和QString內(nèi)部表示相同。但是由于MSVC處于種種考慮,鼓勵大家使用TEXT/_T,反倒使大家對它比較陌生。

但是從C++標(biāo)準(zhǔn)來說,wchar_t畢竟不是char16_t,所以跨平臺性不好。在linux下,這行代碼需要utf32到utf16的轉(zhuǎn)換。

QStringLiteral

這是一個宏,一個蠻復(fù)雜的宏:

QString s4 = QStringLiteral("中文");

之前?

在介紹這個宏之前,我們先看看下面寫法有什么劣勢:

QString s1 = "漢語";QString s2("漢語");QString s3 = tr("中文")QString s6 = u8"中文";//C++11...

首先,2個漢字的字符串以UTF-8編碼的形式被編譯器放到了常量區(qū)。(至少占7個字節(jié)吧?)

然后,程序運行時,構(gòu)造QString實例,需要在堆上申請空間,存放utf16格式的相應(yīng)字符串。

有沒有存在浪費?

方案

QString 內(nèi)部是UTF16,如果C++編譯器在編譯期直接提供了UTF16的字符串,那么我們在QString內(nèi)部直接保存也就夠了。這樣

  • 省掉存在兩份不同的拷貝(即相應(yīng)的轉(zhuǎn)換,malloc的成本)

  • 對漢字來說,UTF16本身就是UTF8省空間

現(xiàn)實

目前,我們還沒有可靠的方式在C++使用UTF16的執(zhí)行字符集(the execution character set)。

  • 盡管 L"..."(wchar_t*) 在Windows下是UTF16,但是不具備跨平臺性。

  • C++11可以保證這一點,u"..."(char16_t),但主流編譯器尚未提供完美支持。

這兩點,導(dǎo)致了QStringLiteral的復(fù)雜性

實現(xiàn)

源碼見 qtbase/src/corelib/tools/qstring.h

(代碼中使用宏、模板、lambda表達式,還是相當(dāng)復(fù)雜的,此處只摘片段)

  • 如果編譯器支持char16_t,則直接使用

#define QT_UNICODE_LITERAL_II(str) u"" strtypedef char16_t qunicodechar;...

  • 否則。如果在Windows平臺下,或者在其他的wchar_t寬度為2的環(huán)境下,使用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)


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI