彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)

彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)


Chapter1 (彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)

原文链接:

这篇文章有点长,内容有点多,如果时间急迫,可以直接翻页去末尾看结论。红色字体加粗的。

一、Qt Creator环境设置

1、cpp或h文件从window上传到Ubuntu后会显示乱码,原因是因为ubuntu环境设置默认是utf-8,Windows默认都是GBK.

我们使用的Windows系统本地字符集编码为GBK。

2、Windows环境下,Qt Creator,菜单->工具->选项->文本编辑器->行为->文件编码->默认编码,常用的选项有以下几个:

System(简体中文windows系统默认指的是GBK编码)

GBK/windows-936-2000/CP936/MS936/windows-936

UTF-8

二、编码知识科普

Qt常见的两种编码是:UTF-8和GBK

★UTF-8:Unicode TransformationFormat-8bit,允许含BOM,但通常不含BOM。是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24为(三个字节)来编码。UTF-8包含全世界所有国家需要用到的字符,是国际编码,通用性强。UTF-8编码的文字可以在各国支持UTF8字符集的浏览器上显示。如果是UTF8编码,则在外国人的英文IE上也能显示中文,他们无需下载IE的中文语言支持包。

★GBK是国家标准GB2312基础上扩容后兼容GB2312的标准。GBK的文字编码是用双字节来表示的,即不论中、英文字符均使用双字节来表示,为了区分中文,将其最高位都设定成1。GBK包含全部中文字符,是国家编码,通用性比UTF8差,不过UTF8占用的数据库比GBD大。GBK是GB2312的扩展,除了兼容GB2312外,它还能显示繁体中文,还有日文的假名。

★GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:

GBK、GB2312--Unicode--UTF8

UTF8--Unicode--GBK、GB2312

★在简体中文windows系统下,ANSI编码代表GBK/GB2312编码,ANSI通常使用0x800xFF范围的2个字节来表示1个中文字符。0x000x7F之间的字符,依旧是1个字节代表1个字符。Unicode(UTF-16)编码则所有字符都用2个字节表示。

三、编码转换

Windows自带的记事本,无法查看UTF-8编码的文件到底有无BOM,需要使用其他文件编辑器,比如EditPlus或者SublimeText。

UTF-8与ANSI(即GBK)的互转,可以使用EditPlus工具"文件另存为"或者Encodersoft编码转换工具对.cpp和.h源文件文本进行批量转换.

四、QString显示中文乱码的原因

我们使用的Windows系统本地字符编码(Local字符集)为GBK。编译器分析出源文件字符编码之后,会进行解码再编码,将源字符集转码成执行字符集。执行字符集一般默认为使用本地字符编码(Local字符集)。

Qt5可以设置Local字符集,GBK/UTF-8

cpp 复制代码
QTextCodec *codec = QTextCodec::codecForName("UTF-8");//或者"GBK",不分大小写
QTextCodec::setCodecForLocale(codec);

Qt5中QString内部采用unicode字符集,utf-16编码。构造函数QString::QString(const char *str)默认使用fromUtf8(),将str所指的执行字符集从utf-8转码成utf-16。

由上面fromUtf8()可知,QString需要执行字符集编码为utf-8,然后以utf-8进行解码,再编码为utf-16才能获得正确的字符编码。显示中文乱码的原因其实就是QString转码方式与执行字符集不一致。(比如,源字符集为本地字符集GBK编码,QString以utf-8的方式进行解码,会导致获得错误的二进制编码,再将错误二进制转为utf-16就会出现乱码。)

五、Qt编码指定

Qt需要在main()函数指定使用的字符编码:

cpp 复制代码
#include <QTextCodec>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    //设置中文字体  
    a.setFont(QFont("Microsoft Yahei", 9));
 
    //设置中文编码
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
#if _MSC_VER
    QTextCodec *codec = QTextCodec::codecForName("GBK");
#else
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
#endif
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    QTextCodec::setCodecForTr(codec);
#else
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QTextCodec::setCodecForLocale(codec);
#endif
 
    return a.exec();
}

这里只列举大家最常用的3个编译器(微软VC++的cl编译器,Mingw中的g++,Linux下的g++),源代码分别采用GBK和无BOM的UTF-8以及有BOM的UTF-8这3种编码进行保存,发生的现象如下表所示。

情况1:指的是Local字符集为GBK

情况2:指的是Local字符集为UTF-8

如果您使用的是Visual C++编译器,则默认情况下不会将您的源代码视为utf-8编码。除非有BOM,否则它将使用您当前的代码页进行解释。就是说,当使用Visual C++编译程序的时候,它会分析源文件采用何种编码,有BOM标识符则可以正确识别其编码是UTF-8,若没有BOM标识符则认为其使用本地字符集编码(Local字符集)。Local字符集是什么?取决于你的设置QTextCodec *codec = QTextCodec::codecForName(???);

如果源文件是UTF-8+BOM的编码方式,还需要在头文件加入

cpp 复制代码
#if defined(_MSC_VER) && (_MSC_VER >= 1600)    
# pragma execution_character_set("utf-8")    
#endif

或者添加QMAKE_CXXFLAGS += /utf-8到您的.pro文件中。

如果源文件是UTF-8+无BOM的编码方式,则一定不能加#pragma execution_character_set("utf-8"),不然会产生乱码。

六、测试案例

案例1、中文字符串测试

cpp 复制代码
#if defined(_MSC_VER) && (_MSC_VER >= 1600)    
# pragma execution_character_set("utf-8")    
#endif
 
#include <QApplication>
#include <QFont>
#include <QTextCodec>
#include <QPushButton>
#include <QDebug>
#include <QString>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    //设置中文字体  
    a.setFont(QFont("Microsoft Yahei", 9));
 
    //设置中文编码
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
#if _MSC_VER
    QTextCodec *codec = QTextCodec::codecForName("gbk");
#else
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
#endif
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    QTextCodec::setCodecForTr(codec);
#else
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
    QTextCodec::setCodecForLocale(codec);
#endif
 
    QString str(QObject::tr("1中文"));
    qDebug() << str;
    qDebug() << QStringLiteral("2中文");
    qDebug() << QString::fromLatin1("3中文");
    qDebug() << QString::fromLocal8Bit("4中文");
    qDebug() << QString::fromUtf8("5中文");
    qDebug() << QString::fromWCharArray(L"6中文");
 
    return a.exec();
}

当QTextCodec::codecForName("utf-8");时,

QString::fromLocal8Bit和QString::fromUtf8是等效的。

当QTextCodec::codecForName("gbk");时,

QString::fromLocal8Bit和QString::fromUtf8是不等效的。

案例2、QCom跨平台串口调试助手(Qt开源社区-致力于Qt普及工作! - qt qml linux 嵌入式 教程!)

源代码qcom\mainwindow.cpp,aboutdialog.cpp等文件用的是UTF-8编码(无BOM);但是qcom\qextserial*.*文件用的是ANSI编码.在linux环境编译完全OK.

笔者Windows环境的Qt Creator+微软VC++编译器,环境设置用的是ANSI(即GBK)编码.编译源文件会报错.

错误提示"fatal error C1018: 意外的 #elif".

解决方法由两种:

方法1:

把qcom\的所有cpp和h文件都用工具转换成ANSI编码,main()函数使用QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));

方法2:

先把Qt Creator环境设置用的是UTF-8编码,

再把qcom\的所有cpp和h文件都用工具转换成UTF-8+BOM编码,请注意,如果文件转换成UTF-8(无BOM),编译仍会失败.main()函数使用QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));//注意,此处仍是"GBK",不是"UTF-8"

重新编译,OK!

其它:

七、结论

Windows环境下,Qt Creator+微软VC++编译器,新建工程,

1、如果该工程不需要跨平台使用(只在win),那么工程设置请使用GBK的编码方式.

2、如果该工程要跨平台使用(win+linux),那么工程设置请使用UTF-8+BOM的编码方式.


另外,还需要在预编译头文件加入

cpp 复制代码
CONFIG += c++11
#可以在项目中使用预编译头文件的支持
CONFIG += precompile_header 
#预编译头文件路径
PRECOMPILED_HEADER = $$PWD/stdafx.h 
cpp 复制代码
#if defined(_MSC_VER) && (_MSC_VER >= 1600)    
# pragma execution_character_set("utf-8")    
#endif

或者添加QMAKE_CXXFLAGS += /utf-8到您的.pro文件中。

3、Linux环境下,Qt Creator+gcc,新建工程,

没有GBK编码可选,默认是UTF-8(无BOM)编码方式,考虑到跨平台,建议选择UTF-8+BOM的编码方式.

★★★★★★★★★★★★综上所述,解决乱码的方法概括如下:★★★★★★★★★★★★

1、如果IDE是Qt Creator,把它的环境设置为"UTF-8+BOM"编码。

2、如果IDE是Visual Studio,请下载插件,名称是ForceUTF8 (with BOM),所有源文件和头文件都会保存为"UTF-8+BOM"编码。

3、如果编译器是MSVC,请在预编译头stdafx.h文件加入

#if defined(_MSC_VER) && (_MSC_VER >= 1600)

pragma execution_character_set("utf-8")

#endif

4、源码文件main函数入口设置中文编码:

cpp 复制代码
#include <QFont>

#include <QTextCodec>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    //设置中文字体  
    a.setFont(QFont("Microsoft Yahei", 9));

//设置中文编码
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
#if _MSC_VER
    QTextCodec *codec = QTextCodec::codecForName("gbk");
#else
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
#endif
    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForCStrings(codec);
    QTextCodec::setCodecForTr(codec);
#else
    QTextCodec *codec = QTextCodec::codecForName("utf-8");
    QTextCodec::setCodecForLocale(codec);
#endif

    MainView w;

    w.show();

    return a.exec();

}

5、如此一来,不管是MSVC编译器还是MinGW编译器,都能编译通过,且支持中文!

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

Chapter2 Qt解决中文显示乱码问题

https://blog.csdn.net/qq_45254369/article/details/125323917

将Text codec for tools 设置为utf-8 即可

设置文件默认编码
1、点击左侧文本编辑器。

2、 第二步,设置文件编码。默认编码选择utf-8, utf-8 bom 设置为 如果有编码是utf-8则添加。

Chapter3 Qt解决中文乱码问题(更新于2023.10.1)

https://blog.csdn.net/qq_73725757/article/details/129645554

Chapter4 QString各种字符编码转化,适用不同Linux系统中文乱码问题

https://blog.csdn.net/qq_45613672/article/details/132457500

Chapter5 qt中文乱码原因分析及解决方案

https://blog.csdn.net/u010810750/article/details/107774481

首先说明几个基础概念

UTF-8 BOM 和 UTF-8。BOM在文件头位置占三个字节,用来标识UTF-8编码,软件通过BOM来识别这个文件是否是UTF-8编码。

源码字符集。源码文件使用某种编码格式保存。

执行字符集。可执行程序内保存的编码(程序执行时内存中字符串编码)。
QString中统一采用utf-16(Unicode)存储字符串。

QString::fromxxx()函数。代表将字符串以对应格式去解析成Unicode,fromLocal8Bit()代表使用本地字符集(系统设置)转换成Unicode;fromUtf8代表从utf-8转换成Unicode。Qt程序运行的时候字符串编码在QString中实际都是以unicode编码存储的,通过使用QString::fromxxx()转换成转换成unicode存到QString中。

乱码产生的原因
中文处理的时候,编译器首先解析源码字符集,根据具体情况将源码字符集解析为执行字符集,再将执行字符集转换为QString的编码(unicode)。QString的编码是固定的,要想整个过程无乱码,那么源码字符集到执行字符集的转换必须确定。换句话说就是编译器必须明确文件的编码和执行字符集。这个不同的编译器的逻辑是不一样的。以MSVC和Mingw为例分析:

MSVC
源码字符集:源文件有BOM的情况下按BOM解释,无则使用本地Locale字符集(随系统设置而变)。

执行字符集:使用本地Locale字符集(随系统设置而变)。

Mingw
源码字符集和执行字符集默认都按utf-8解析。

因此乱码的原因有:

编译器解读源码字符集错误,比如utf-8的源码不带bom编译器会当成locale,执行字符集也是locale所以不需要转换,但是实际utf-8的编码到locale是需要转换的。

源码字符集到执行字符集的转换错误。比如需要解析执行字符集utf-8,需要使用fromUtf8。但是却使用了其它的转换方式fromXXX,这是使用了错误的转换算法。

字符解析错误。比如程序中的字符串二进制是utf-8的,设置执行字符集是loacle,那么解析就会出错。

解决方案

最好的解决方案是将源码保存成utf8 bom,执行字符集也选为utf8

对于MSVC,源码字符集之所以保存成 bom是因为只有bom 编译器才能识别出来源码字符集,执行字符集设置为utf8在C++98的MSVC上是没有什么好办法的,只能使用QString::fromUtf8设置转换算法。c++ 11比较方便,提供了设置执行字符集的方法 #pragma execution_character_set("utf-8")。

之所以说这是最好的解决方案,是因为这种设置在qt5以上的版本中具有良好的跨平台性,Mingw和GCC都支持这种方案。

采用这种方案无需再写QString::fromxxx()函数(此中情况下发现无论是在msvc还是mingw下,对于中文常量的处理加不加fromUtf8都能正常处理,分析了一部分源码和注释后猜测是因为从const char*到QString的隐式转换内部使用了QStringData保存字符串的空间,然后这个字符串 从 utf-8转码为unicode并拷贝到data)。

使用vs2010以上版本开发程序的时候,可以安装ForceUTF8(with BOM)插件,能够使新创建的工程文件都是BOM格式。

#pragma execution_character_set("utf-8")不需要硬编码到代码中,可以使用inc文件在pri文件中设置编译器和链接器flag参数即可

将#pragma execution_character_set("utf-8")保存在xxx.inc中

在pri中添加:

win32-msvc*:QMAKE_CXXFLAGS += -FI"$$PWD/xxx.inc"

win32-msvc*:QMAKE_CFLAGS += -FI"$$PWD/xxx.inc"

另外有一点需要注意,编译器根据ui文件生成的ui_xxx.h文件默认编码是utf-8(无BOM),里面涉及到的汉字都已经转换成了字符编码,所以ui文件生成的源码无需特殊处理。

总结

Qt开发环境统一设置(设置两个地方:环境和文本剪辑器),默认编码选择utf-8, utf-8 bom 设置为 如果有编码是utf-8则添加。


在需要显示中文的地方使用QStringLiteral宏处理一下。

cpp 复制代码
ui->label_2->setText(QStringLiteral("这是UTF-8 BOM编码的中文显示"));

QStringLiteral是一个宏,在支持lambda表达式的c++11中,直接解析源码字符集到宽字节。不支持lambda的编译环境中等同于fromUtf8,只要保证源码字符集编译器识别正确就不会出现乱码,因为它是直接转换成了宽字符,和执行字符集没有关系(个人认为)。

相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript