MSVC 中的utf-8编码问题 / Qt中文乱码问题

当我们在VS里面编译一个以UTF8保存的c源程序时,MSVC会将字符串的编码从UTF8转换为ANSI编码(当前系统的页代码)

我们通过运行以下代码进行测试

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
 const char * str = "a哈は";
 int i;
 for (i = 0; i < strlen(str); i++)
 printf("%02hhX ", (unsigned char)str[i]);
 printf("\n%s\n", str);
 system("pause");
 return 0;
}

"a哈は"如果在ANSI编码下的十六进制内容应该是:61 B9 FE A4 CF

在UTF-8编码下应该是:61 E5 93 88 E3 81 AF

在不同编码的不同环境运行下有以下的输出结果:

编译器 UTF-8 BOM UTF8 ANSI
MSVC 14.0 61 B9 FE A4 CF(正常) 61 E5 93 88 E3 81 AF(乱码) 61 B9 FE A4 CF(正常)
mingw5.5 61 E5 93 88 E3 81 AF(乱码) 61 E5 93 88 E3 81 AF(乱码) 61 B9 FE A4 CF(正常)

*Windows控制台默认编码(页代码)是GBK,输出内容不是GBK编码的都会是乱码

可以得出以下几个结论:

  • 源程序不包含BOM头时,MSVC会认为程序的编码是当前的”执行字符集”即ANSI编码(当前系统的页代码是GBK)
    这次的字符串在UTF8编码下恰好是6个字节,可以构成3个汉字,所以编译器没将后面的”(双引号)当作汉字的一部分处理。  如果内容是"aは"(61 E3 81 AF)编译时会将最后的AF与双引号(“)结合,然后就会有各种奇葩的错误了
  • 当源程序含有UTF8 BOM头时,MSVC会认为程序是UTF8编码,遇到字符串时会把字符串转换为ANSI编码即当前系统的页代码(GBK)。所以实际上在运行时候,字符串还会是GBK编码。
  • mingw在编译时,默认会将字符串和程序当作UTF8编码(这也是因为它是从unix系统移植过来的吧)

实际应用:

Qt中文乱码问题

在Qt中,如果我们有下面的代码

QMessageBox::information(NULL,"哈","は");

在用MSVC编译器编译,默认情况下编译会出现错误(UTF8 无BOM):

mainwindow.cpp:-1: warning: C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失

这是因为某个汉字的utf8编码被拆为若干个汉字,最后一个字符恰好跟双引号结合起来了。

如果程序是UTF8有BOM 编码,编译没有问题,但是运行时内容是乱码。

原因是在你传入一个字符串时候,Qt默认以UTF8编码*将char转为QChar。

*作者注:网上许多地方说默认是西欧编码,在5.10版本的Qt,官网的描述如下
Constructs a string initialized with the 8-bit string str. The given const char pointer is converted to Unicode using the fromUtf8() function.
大概意思是默认从UTF8编码转换为QString
You can disable this constructor by defining QT_NO_CAST_FROM_ASCII when you compile your applications. This can be useful if you want to ensure that all user-visible strings go through QObject::tr(), for example.
Note: Defining QT_RESTRICTED_CAST_FROM_ASCII also disables this constructor, but enables a QString(const char (&ch)[N]) constructor instead. Using non-literal input, or input with embedded NUL characters, or non-7-bit characters is undefined in this case.
如果声明常量QT_RESTRICTED_CAST_FROM_ASCII,那么输入的字符串应该都是ASCII字符,总而言之就是英文字符串。

小提示:

在Qt Creator的 工具->选项->文本编辑器->行为->文件编码   将UTF8-BOM选为 如果编码是UTF8则添加  然后保存文件可以为源文件添加BOM头

 

 

如果你打算用MSVC继续编译,本文列出以下几个解决方案:

 

一、在编译时加入编码选项(仅限VS2015 Update 2以上版本)(推荐!)

文件保存为UTF8格式(无论有没有BOM都可以)

在xxx.pro文件最下面加入下面代码:

QMAKE_CXXFLAGS += /source-charset:utf-8 /execution-charset:utf-8

然后在qt creator的 构建 -> 执行qmake 后再编译运行就正常了

二、在源文件加入编码选项(仅限VS2010以上版本)(比较推荐)

文件保存为UTF8带BOM格式

在含有非英文字符的文件最开头加入代码:

#pragma execution_character_set("utf-8")

三、可以使用QString::fromLocal8Bit,那么Qt会将字符串从使用QTextCodec::codecForLocale设置的编码转为Unicode编码

使用举例:

#include <QTextCodec> 
/*....*/ 
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));//默认的Codec是System(当前系统的编码) 
QMessageBox::information(NULL,QString::fromLocal8Bit("哈"),QString::fromLocal8Bit("は"));

如果我们不设置QTextCodec::setCodecForLocale,那么我们的程序在非GBK环境下运行也可能会出现乱码

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据