天天看點

VC2010下Qt5的中文亂碼問題回到 Qt5 的中文輸出問題。

要搞清楚這個問題,先要弄明白編碼。但是編碼問題實在太複雜,這裡肯定講不開。

我先找一個例子,比如:“中文” 的 Unicode 碼點/UTF8編碼/GBK 分别是多少。

先去這個網站,輸入 “中文” 查詢對應的 Unicode 碼點/UTF8編碼: 

<a href="http://www.mytju.com/classcode/tools/encode_utf8.asp">http://www.mytju.com/classcode/tools/encode_utf8.asp</a>

Unicode的碼點分别是(十進制):中(20013),文(25991)。 

對應的UTF8編碼分别(16進制): 中(E4B8AD),文(E69687)。

然後再去下面這個網站,輸入 “中文” 查詢對應的 GBK 編碼: 

<a href="http://www.mytju.com/classcode/tools/encode_gb2312.asp">http://www.mytju.com/classcode/tools/encode_gb2312.asp</a>

GBK編碼16進制(GBK内碼)分别是:中(D6D0),文(CEC4)。

現在已經知道了"中文"的UTF8和GBK編碼的具體值。 

我們再看看VC2010是怎麼處理的。

<code>01</code>

<code>// utf8 no bom</code>

<code>02</code>

<code>// 檔案中包含不能在目前代碼頁(936)中表示的字元</code>

<code>03</code>

<code>#include &lt;stdio.h&gt;</code>

<code>04</code>

<code>05</code>

<code>int</code> <code>main() {</code>

<code>06</code>

<code>    </code><code>const</code> <code>char</code><code>* str = </code><code>"中文"</code><code>;</code>

<code>07</code>

<code>    </code><code>for</code><code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; sizeof(str); ++i) {</code>

<code>08</code>

<code>        </code><code>printf(</code><code>"0x%x "</code><code>, str[i]&amp;</code><code>0xFF</code><code>);</code>

<code>09</code>

<code>    </code><code>}</code>

<code>10</code>

<code>    </code><code>return</code> <code>0</code><code>;</code>

<code>11</code>

<code>    </code><code>// Output:</code>

<code>12</code>

<code>    </code><code>// 0xe4 0xb8 0xad 0xe6</code>

<code>13</code>

<code>}</code>

輸出是:0xe4 0xb8 0xad 0xe6。 

感覺好像是對的。

但是,先别急:VC編譯時輸出了一條警告資訊: 

utf8_no_bom.cpp : warning C4819: 該檔案包含不能在目前代碼頁(936)中表示的字元。 

請将該檔案儲存為 Unicode 格式以防止資料丢失。

潛台詞就是,你這個代碼有GBK不能表示的字元,請用Unicode方式儲存。 

VC根本就沒把 代碼(utf8_no_bom.cpp) 當作UTF8,VC隻是把它作為GBK處理罷了。

那為什麼又輸出了正确的結果呢?

因為 VC 把 (utf8_no_bom.cpp) 當作 GBK,而編譯時也要轉換為本地編碼(也是GBK)。 

是以,UTF8編碼的 “中文”,被VC當作編碼為 “0xe4 0xb8 0xad 0xe6” 的其他中文處理了。 

VC已經不知道 “0xe4 0xb8 0xad 0xe6” 是對應 “中文” 字面值了。

但是在GBK(實際是無BOM的UTF8)轉GBK的過程中,發現了一些UTF8編碼的字元并不是 

GBK能表達的合理方式,是以就出現了那個C4819編譯警告。

<code>// utf8 with bom</code>

<code>    </code><code>// 0xd6 0xd0 0xce 0xc4</code>

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

源檔案明明是 UTF8 編碼的格式"0xe4 0xb8 0xad 0xe6”, 

怎麼變成了 “0xd6 0xd0 0xce 0xc4” (這個是GBK編碼)?

這就是VC私下幹的好事:它自作聰明的将UTF8源代碼轉換為GBK處理了!

VC為何要做這樣蠢事?

原因是為了相容老的VC版本。 

因為以前的VC不能處理UTF8,都是用本地編碼處理的。

<code>// gbk</code>

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

因為源檔案就是GBK,cl在編譯時GBK轉化為GBK,沒有改變字元串。

隻是,現在很多人不想用GBK了(因為隻能在中國地區用,不能表示全球字元)。

到這裡,可以初步小結一下:

VC編輯器和VC編譯器是2個概念,VC編輯器支援UTF8并不能表示VC編譯器也支援UTF8

VC編輯器從2008?開始支援帶BOM的UTF8(不帶BOM的暫時沒戲,因為會本地編碼沖突)

VC編譯器從2010開始重要可以支援UTF8了(雖然支援方式很不優雅)

VC2010重要增加了UTF8的編譯支援(<code>#pragma execution_character_set("utf-8")</code>), 

具體檢視:

<a href="http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/2f328917-4e99-40be-adfa-35cc17c9cdec">http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/2f328917-4e99-40be-adfa-35cc17c9cdec</a>

<code>// utf8 with bom (VC2010), 這句是重點!</code>

<code>#pragma execution_character_set(</code><code>"utf-8"</code><code>)</code>

<code>14</code>

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

UTF8編碼,UTF8輸出。完美!

Qt預設支援 VS2010/MinGW/Gcc 等編譯器,而它們現在都已經真正支援UTF8了。

當然,VS2010 對UTF8的支援會入侵代碼(<code>#pragma execution_character_set("utf-8")</code>)。

看看Qt官方論壇别人是怎麼說的: 

<a href="http://qt-project.org/forums/viewthread/17617">http://qt-project.org/forums/viewthread/17617</a>

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 <code>#include &lt;QApplication&gt;</code> <code>  </code><code>#include &lt;QLabel&gt;</code> <code>  </code><code>#</code><code>if</code> <code>_MSC_VER &gt;= </code><code>1600</code> <code>  </code><code>#pragma execution_character_set(</code><code>"utf-8"</code><code>)</code> <code>  </code><code>#endif</code> <code>  </code><code>int</code> <code>main(</code><code>int</code> <code>argc, </code><code>char</code> <code>*argv[])</code> <code>  </code><code>{</code> <code>      </code><code>QApplication a(argc, argv);</code> <code>      </code><code>QLabel label(</code><code>"ąśćółęńżź"</code><code>);</code> <code>      </code><code>label.show();</code> <code>      </code><code>return</code> <code>a.exec();</code> <code>15</code> <code>  </code><code>}</code> If other people can reproduce your problem, you can file a bug.

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

<code>// Coding: UTF-8(BOM)</code>

<code>#</code><code>if</code> <code>defined(_MSC_VER) &amp;&amp; (_MSC_VER &gt;= </code><code>1600</code><code>)</code>

<code># pragma execution_character_set(</code><code>"utf-8"</code><code>)</code>

<code>#endif</code>

<code>#include &lt;QTextCodec&gt;</code>

<code>#include &lt;QLabel&gt;</code>

<code>int</code> <code>main(</code><code>int</code> <code>argc, </code><code>char</code><code>* argv[])</code>

<code>{</code>

<code>    </code><code>QApplication app(argc, argv);</code>

<code>#</code><code>if</code> <code>QT_VERSION &lt; QT_VERSION_CHECK(</code><code>5</code><code>,</code><code>0</code><code>,</code><code>0</code><code>)</code>

<code>#</code><code>if</code> <code>defined(_MSC_VER) &amp;&amp; (_MSC_VER &lt; </code><code>1600</code><code>)</code>

<code>16</code>

<code>    </code><code>QTextCodec::setCodecForTr(QTextCodec::codecForName(</code><code>"GB18030-0"</code><code>));</code>

<code>17</code>

<code>#</code><code>else</code>

<code>18</code>

<code>    </code><code>QTextCodec::setCodecForTr(QTextCodec::codecForName(</code><code>"UTF-8"</code><code>));</code>

<code>19</code>

<code>20</code>

<code>21</code>

<code>22</code>

<code>    </code><code>QLabel *label = </code><code>new</code> <code>QLabel(QObject::tr(</code><code>"你好!"</code><code>));</code>

<code>23</code>

<code>    </code><code>label-&gt;show();</code>

<code>24</code>

<code>25</code>

<code>    </code><code>return</code> <code>app.exec();</code>

<code>26</code>

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

Qt5+/VC2010+: 包含了 <code># pragma execution_character_set("utf-8")</code> 已經支援中文

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

Qt4+/VC2008-: 采用以前老的方式, 指定代碼為 “GB18030-0” 編碼

Qt4/Qt5/Linux: 隻要是預設的UTF8環境, 應該都沒問題

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

即使是支援UTF16的Java也同樣難逃此問題.

我在電腦上找了一個顯示中文.ttf檔案加到了QtSDk-&gt;Simulator-&gt;Application-&gt;fonts中就可以解決了

QQ:519841366

本頁版權歸作者和部落格園所有,歡迎轉載,但未經作者同意必須保留此段聲明,

且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利

繼續閱讀