VC2010下Qt5的中文亂碼問題

要搞清楚這個問題,先要弄明白編碼。可是編碼問題實在太複雜,這裏確定講不開。app

我先找一個例子,好比:"中文" 的 Unicode 碼點/UTF8編碼/GBK 分別是多少。編輯器

先去這個網站,輸入 "中文" 查詢對應的 Unicode 碼點/UTF8編碼:http://www.mytju.com/classcode/tools/encode_utf8.asp 網站

Unicode的碼點分別是(十進制):中(20013),文(25991)。 對應的UTF8編碼分別(16進制): 中(E4B8AD),文(E69687)。this

而後再去下面這個網站,輸入 "中文" 查詢對應的 GBK 編碼:http://www.mytju.com/classcode/tools/encode_gb2312.asp 編碼

GBK編碼16進制(GBK內碼)分別是:中(D6D0),文(CEC4)。code

如今已經知道了"中文"的UTF8和GBK編碼的具體值。 咱們再看看VC2010是怎麼處理的。utf-8

1. 先看 無 BOM 的 UTF8 編碼的代碼 (utf8_no_bom.cpp)

// utf8 no bom
// 文件中包含不能在當前代碼頁(936)中表示的字符
#include <stdio.h>

int main() {
    const char* str = "中文";
    for(int i = 0; i < sizeof(str); ++i) {
        printf("0x%x ", str[i]&0xFF);
    }
    return 0;
    // Output:
    // 0xe4 0xb8 0xad 0xe6
}

輸出是:0xe4 0xb8 0xad 0xe6。 感受好像是對的。ci

可是,先別急:VC編譯時輸出了一條警告信息: utf8nobom.cpp : warning C4819: 該文件包含不能在當前代碼頁(936)中表示的字符。 請將該文件保存爲 Unicode 格式以防止數據丟失。字符串

潛臺詞就是,你這個代碼有GBK不能表示的字符,請用Unicode方式保存。 VC根本就沒把 代碼(utf8nobom.cpp) 看成UTF8,VC只是把它做爲GBK處理罷了。get

那爲何又輸出了正確的結果呢?

由於 VC 把 (utf8nobom.cpp) 看成 GBK,而編譯時也要轉換爲本地編碼(也是GBK)。 所以,UTF8編碼的 "中文",被VC看成編碼爲 "0xe4 0xb8 0xad 0xe6" 的其餘中文處理了。 VC已經不知道 "0xe4 0xb8 0xad 0xe6" 是對應 "中文" 字面值了。

可是在GBK(實際是無BOM的UTF8)轉GBK的過程當中,發現了一些UTF8編碼的字符並非 GBK能表達的合理方式,所以就出現了那個C4819編譯警告。

2. 再看帶BOM的UTF8是怎麼處理的 (utf8_with_bom.cpp)

// utf8 with bom
#include <stdio.h>

int main() {
    const char* str = "中文";
    for(int i = 0; i < sizeof(str); ++i) {
        printf("0x%x ", str[i]&0xFF);
    }
    return 0;
    // Output:
    // 0xd6 0xd0 0xce 0xc4
}

編譯沒有警告,可是輸出有問題:0xd6 0xd0 0xce 0xc4。

源文件明明是 UTF8 編碼的格式"0xe4 0xb8 0xad 0xe6", 怎麼變成了 "0xd6 0xd0 0xce 0xc4" (這個是GBK編碼)?

這就是VC私下乾的好事:它自做聰明的將UTF8源代碼轉換爲GBK處理了!

VC爲什麼要作這樣蠢事?

緣由是爲了兼容老的VC版本。 由於之前的VC不能處理UTF8,都是用本地編碼處理的。

3. 在看看真的GBK是怎麼處理的 (gbk.cpp)

// gbk
#include <stdio.h>

int main() {
    const char* str = "中文";
    for(int i = 0; i < sizeof(str); ++i) {
        printf("0x%x ", str[i]&0xFF);
    }
    return 0;
    // Output:
    // 0xd6 0xd0 0xce 0xc4
}

沒有編譯錯誤,輸出也和源代碼一致:"0xd6 0xd0 0xce 0xc4"。

由於源文件就是GBK,cl在編譯時GBK轉化爲GBK,沒有改變字符串。

只是,如今不少人不想用GBK了(由於只能在中國地區用,不能表示全球字符)。

到這裏,能夠初步小結一下:

  1. VC編輯器和VC編譯器是2個概念,VC編輯器支持UTF8並不能表示VC編譯器也支持UTF8

  2. VC編輯器從2008?開始支持帶BOM的UTF8(不帶BOM的暫時沒戲,由於會本地編碼衝突)

  3. VC編譯器從2010開始重要能夠支持UTF8了(雖然支持方式很不優雅)

4. 看看VC2010是怎麼處理帶BOM的UTF8的 (utf8_with_bom_2010.cpp)

VC2010重要增長了UTF8的編譯支持(#pragma execution_character_set("utf-8")), 具體查看:

http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/2f328917-4e99-40be-adfa-35cc17c9cdec

// utf8 with bom (VC2010), 這句是重點!
#pragma execution_character_set("utf-8")

#include <stdio.h>

int main() {
    const char* str = "中文";
    for(int i = 0; i < sizeof(str); ++i) {
        printf("0x%x ", str[i]&0xFF);
    }
    return 0;
    // Output:
    // 0xe4 0xb8 0xad 0xe6
}

沒有編譯錯誤,輸出也和源代碼一致:"0xe4 0xb8 0xad 0xe6"。

UTF8編碼,UTF8輸出。完美!

回到 Qt5 的中文輸出問題。

Qt默認支持 VS2010/MinGW/Gcc 等編譯器,而它們如今都已經真正支持UTF8了。

固然,VS2010 對UTF8的支持會入侵代碼(#pragma execution_character_set("utf-8"))。

看看Qt官方論壇別人是怎麼說的: http://qt-project.org/forums/viewthread/17617

Nothing special need to do, it will works by default. If the exec-charset of your your compiler is UTF-8.

簡單的說,從Qt5開始,源代碼就是默認UTF8編碼的。

固然,VC2010編輯器對帶BOM的UTF8也是認識,只惋惜VC2010編譯器根本認可它是UTF8!

在繼續看官方論壇的回覆:

You can write a simple example like this

#include <QApplication>
  #include <QLabel>

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

  int main(int argc, char *argv[])
  {
      QApplication a(argc, argv);
      QLabel label("ąść&oacute;łęńżź");
      label.show();

      return a.exec();
  }

If other people can reproduce your problem, you can file a bug.

教完整的解決方案(增長了Qt4/Qt5和非VC環境的判斷):

// Coding: UTF-8(BOM)
#if defined(_MSC_VER) && (_MSC_VER >= 1600)
# pragma execution_character_set("utf-8")
#endif

#include <QApplication>
#include <QTextCodec>
#include <QLabel>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
#if defined(_MSC_VER) && (_MSC_VER < 1600)
    QTextCodec::setCodecForTr(QTextCodec::codecForName("GB18030-0"));
#else
    QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
#endif
#endif

    QLabel *label = new QLabel(QObject::tr("你好!"));
    label->show();

    return app.exec();
}

有如下幾種類型(源代碼必須是帶BOM的UTF8):

  • Qt5+/VC2010+: 包含了 # pragma execution_character_set("utf-8") 已經支持中文

  • Qt5/VC2008-: 這個暫時誤解(我還沒找到方法)

  • Qt4+/VC2008-: 採用之前老的方式, 指定代碼爲 "GB18030-0" 編碼

  • Qt4/Qt5/Linux: 只要是默認的UTF8環境, 應該都沒問題

其實這個問題不是Qt特有的, 追根溯源仍是C/C++和編譯器的問題.

相關文章
相關標籤/搜索