在Qt5.6中處理VC帶來的亂碼問題

轉載:https://my.oschina.net/fanhuazi/blog/740594

本文假設你所使用的開發環境爲Qt5.6+vs2015,爲了能夠復現亂碼的問題我們先隨便建立一個Qt的空工程,添加一個main.cpp文件(UTF8格式),使用qDebug(),和std::count輸出一句漢語“我是中國人”:

#include <qdebug.h>
#include <iostream>


void main()
{
    //使用Qt的qDebug()輸出
    qDebug()<<"qdebug:"<<"我是中國人";

    //使用C++標準庫輸出
    std::cout<<"std count:"<<"我是中國人"<<std::endl;

}

結果如下所示:

std count:我是中國人
qdebug: ?????й???

使用C++的標準輸出(std::cout)輸出了正確的結果,而使用Qt的qDebug()則沒有得到正確的結果,彷彿Qt的“兼容性”不如C++似的,首先我們要搞懂字符串的編譯過程:

(1)程序員在文本文件中書寫字符串,事實上源文件中所有的東西都是字符串,需要“傳輸到”到程序中進行處理的是使用雙引號括起來的“我是中國人”。此時“我是中國人”在文本文件中按照UTF-8格式存儲。

(2)編譯器(Linux爲G++,vc爲cl.exe)讀取源文件的內容並進行詞法分析,此時字符串的內容被轉換爲編譯器的內部格式,有的朋友喜歡討論VC具體使用什麼編碼,GCC什麼編碼,在此這些討論時毫無意義的,因爲編譯器會按照廠家的商業需要隨時改變其內部格式。此時,例子中使用的VC編譯器在中國使用的編碼爲GBK,於是“我是中國人”從UTF-8格式轉換爲GBK格式存儲在編譯器的“緩存”裏面。注意,如果編譯器按照錯誤的編碼格式讀取源文件,那麼任何字符串都將被解釋爲錯誤的亂碼。常規的UTF-8編碼通過添加BOM標記可以完美的解決這一問題。

(3)編譯器將字符串的內容轉換爲目標文件中的全局變量,“我是中國人”這五個字在源碼中形象的被稱之爲“存儲在全局數據區的類型爲const char *的字符串”,直到此時,這五個字才轉變爲“const char *”存儲在全局數據區。這個時段,編譯器(說的就是msvc)想當然地把GBK版本的“我是中國人”依舊存儲爲GBK格式

(4)在運行期間,程序對字符串的格式完全處於無知的狀態,顯然 const char *是沒有表露任何格式信息的。但是爲什麼std::cout卻能夠輸出完全正確的結果呢?神探狄仁傑曾經說過,這世界上根本沒有巧合,只有精心策劃的陰謀:微軟當然不會自己編寫程序把自己的編譯器搞出來的const char *輸出亂碼!呵呵!在Qt4的時代,用戶可以在需要格式話加載字符串的場合之前調用以下代碼來顯式的指定const char *的編碼:

QTextCodec::setTextCodcForTr(...)

想想也知道這又多麻煩,萬一有幾處代碼沒有設置,豈不麻煩。在Qt5的時期,Qt官方果斷的刪除了這一系列的函數,所有的轉換const char *的動作均按照UTF-8處理。

根據以上的論述,要保證輸出不亂碼,總結需要保證四條:

(1)源文件格式清楚明白(作者自己必須心裏有數)(2)編譯器按照正確格式讀取;(3)編譯器生成全局const char *的格式要搞明白;(4)全局const char *的格式和Qt的字符串加載所需格式對上。

解決(1),(2)的內容,需要設置編輯器文件編碼,在QtCreator的設置如下圖所示,如果你的源文件不是UTF-8建議批量轉成UTF-8

 

由於Qt已經明確要求全局數據去的字符串必須是UTF-8格式了,那麼就必須通過編譯器來設置,在使用VC的情況下,加入這些代碼就成生效,前提是用的VS是2012以上的版本(相信大家都已經用新的了):

#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif

即便不是天才也能看出來這個做法比設置QTextCodec還要麻煩,難道每個文件都要添家三句代碼嗎?好在Qt的工程管理中已經支持將一個頭文件預編譯,可以通過這種機制讓工程中所有的源文件全部在編譯時段自動的包含同一個頭文件。

新建一個文件,設名稱爲utf8.h,將pragma命令語句添加里面,然後在Qt的工程文件中添加:

#設置編碼統一使用utf8
PRECOMPILED_HEADER += $$GTDIR/include/utf8.h

將整個代碼工程重新qmake,重新編譯,亂碼一去不復返,且對代碼無污染

 

講了上面那麼多理論,我們用剛纔的小工程測試一下,main函數變更爲:

#include <qdebug.h>
#include <iostream>

#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif


void main()
{
    //使用Qt的qDebug()輸出
    qDebug()<<"qdebug:"<<"我是中國人";

    //使用C++標準庫輸出
    std::cout<<"std count:"<<"我是中國人"<<std::endl;

}

輸出結果爲下圖所示:

std count:鎴戞槸涓浗浜
qdebug: 我是中國人

qDebug好了,std::cout確亂了,沒辦法,誰叫他們用的格式不一樣呢,當然在這裏,Qt是大局。相信你不會用std::cout再輸出東西的。

如果非要使用stdout輸東西,一定記得轉換爲本地格式,不要計較本地格式是啥,QString的toLocal8bit會幫你做判斷的,因此,將代碼改爲:

#include <qdebug.h>
#include <iostream>

#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif


void main()
{
    //使用Qt的qDebug()輸出
    qDebug()<<"qdebug:"<<"我是中國人";

    //使用C++標準庫輸出
    std::cout<<"std count:"<<QString("我是中國人").toLocal8Bit().data()<<std::endl;

}

我們得到了完全正確的輸出:

std count:我是中國人
qdebug: 我是中國人

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章