我们以“测试字符串-보고싶다-Test String.”这个字符串来进行讲解,它包含了英文、中文和韩文。
因为我使用 Qt 的方式是Visual Studio + Qt库的形式,所以本文以Visual StudioMSVC编译器为例来进行讲解,但这种方式的原理也适用于其他编译器。

QString 中使用 QChar 来存储每一个字符,QChar 是 short 类型,占 2 个字节,默认按 Unicode 编码存储。

首先,为了保证写到代码文件中的测试字符串能被 MSVC 编译器理解,我们需要将源文件保存为Utf8-带签名的格式。具体参考:拨开字符编码的迷雾(2)--编译器处理文件编码

解决Qt程序乱码问题的关键在于理解QString中存储字符的编码格式。

QString 中存储的字符串的编码格式就是“编译器执行字符集编码格式”。 这一句话很关键。

在 MSVC 中我们可以使用#pragma execution_character_set("utf-8")来指定该源文件的执行字符集编码格式为 UTF8 格式,这样 QString 中存储的字符串格式就是 utf8 编码了。

下面是完整的测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
void Demo01::qStringUseCase() {
/*
该源文件使用Utf8-BOM格式保存.
源字符集为UTF8-BOM.
可执行字符集为UTF8:#pragma execution_character_set("utf-8") 定义在stdafx.h.
*/

#define TEST_STR_A "测试字符串-보고싶다-Test String."
#define TEST_STR_W L"测试字符串-보고싶다-Test String."

{
// 因为可执行字符集是utf8,所以使用OutputDebugStringA需要转换成ANSI
OutputDebugStringA(Utf8ToAnsi(TEST_STR_A).c_str());
}

{
ui.lblLanguage->setText(TEST_STR_A);
}

// char* --> QString
{
QString qstr = TEST_STR_A;
qInfo() << qstr;
}

// QString --> char*或std::string
{
QString qstr = TEST_STR_A;

std::string str = qstr.toStdString(); // 不能直接使用qstr.toStdString().c_str()来获取,必须先将qstr.toStdString()存入std::string中
const char* pStr = str.c_str();


QString qstr2 = QString::fromUtf8(pStr);
Q_ASSERT(qstr == qstr2);
}

// QString --> wchar_t*或std::wstring
{
QString qstr = TEST_STR_A;

std::wstring str = qstr.toStdWString();
const wchar_t * pStr = str.c_str();

QString qstr2 = QString::fromWCharArray(pStr);
Q_ASSERT(qstr == qstr2);
}

// std::string --> QString
{
std::string str = TEST_STR_A; // std::string中存储的是UTF-8编码的字符串,因为可执行字符集为UTF8
QString qstr = QString::fromStdString(str);
}

// std::wstring --> QString
{
std::wstring str = TEST_STR_W; // std::wstring中存储的是UTF-8编码的字符串,因为可执行字符集为UTF8
QString qstr = QString::fromStdWString(str);
}
}

QString::toLocal8Bit

QString 有一个名为toLocal8Bit的方法,网上很多介绍如何解决乱码的文章都会提到这个函数。关于这个函数官方的介绍如下:

1
2
3
4
5
Returns the local 8-bit representation of the string as a QByteArray. The returned byte array is undefined if the string contains characters not supported by the local 8-bit encoding.

QTextCodec::codecForLocale() is used to perform the conversion from Unicode. If the locale encoding could not be determined, this function does the same as toLatin1().

If this string contains any characters that cannot be encoded in the locale, the returned byte array is undefined. Those characters may be suppressed or replaced by another.

简单来说,我们可以理解为这个函数将字符串转换为的 ANSI 编码,通过拨开字符编码的迷雾(1)--字符编码概述介绍,我们应该知道ANSI是和具体的代码页相关联的(在 Windows 中文环境下默认代码页为 936)。Qt 不是根据系统代码页来做判断的,而是通过QTextCodec来做判断的,所以文档中会提到这个函数需要结合QTextCodec::codecForLocale()来使用,toLocal8Bit根据对应的QTextCodec来做相应的转换。

总结

所以要想在使用 Qt 时,避免遇到中文乱码问题,只需要在预编译头文件中加入(对于不使用预编译头的项目可以在.cpp文件中添加):

1
#pragma execution_character_set("utf-8")

同时由于部分韩文、日文等字符不在 Visual Studio 默认的中文 GB2312 编码中,所以如果遇到 Visual Studio 提示“此文件的某些 Unicode 字符未能保存到当前代码页中”时,这时应该选择”Utf8-带签名“格式来保存。

综上所述,源文件保存为Utf8-带签名,且设置#pragma execution_character_set("utf-8") 就可以解决所有乱码问题。

文章图片带有“CSDN”水印的说明:
由于该文章和图片最初发表在我的CSDN 博客中,因此图片被 CSDN 自动添加了水印。